0

I have a static UITableView built from a Storyboard that works well. I want to fill the first category programmatically, though, from a user-defined file

Simply put, I want to go through all the strings in an array and add them as cells for the rows of the first category. For the second category, I have a series of mildly complex cells (containing a number of labels, textfields, buttons and other controls), defined in the storyboard, that I don't feel like recreating in code.

As far as I understand, the default behaviour for a UITableView built from a storyboard is to use the nib file as an implicit datasource. If I use a custom class as datasource, my second section doesn't work. I have thought of two possible ways to fix this:

  • Fill my first category from the datasource and delegate the rest to the nib file. Is this possible? Is there some method to programmatically ask the nib to fill my UITableView?
  • Export my storyboard-built cells into code and paste this code into my datasource. This method has the disadvantage of making my second category harder to modify.

Is one of those two options feasible? Is there another option?

LodeRunner
  • 7,975
  • 3
  • 22
  • 24

2 Answers2

1

There are two kinds of table views when you use Storyboards:

  • Static
  • Dynamic

You're currently using the former. You define everything in the Storyboard and have very little code.

But you need to change to the latter.

You can still keep your UITableViewCells in the Storyboard; there's no need to do that in code (though you can if it makes things easier). You can refer to the template cells using the "reuse identifer."

Otherwise you've pretty much got it. You'll need to write code to implement the data source and (possibly) more methods of the table view delegate.

It's kind of fiddly switching from static to dynamic. I keep meaning to raise a Radar because I'm sure Xcode could be making it easier to do...

Stephen Darlington
  • 51,577
  • 12
  • 107
  • 152
  • Great! Dynamic table + reuse identifier seems to work perfect to build my complex cells in the Storyboard. I have another problem, though. My textfields and controls from the static cells were connected to my controller via `IBOutlet`s. Now, how do I connect them? I need to fill them with default values, and after the user pushes a button, fetch those values and use them. Any hints? – LodeRunner Jul 20 '12 at 13:51
  • 1
    You can't do it in the same way. You need to create a UITableViewCell subclass which has the IBOutlet/IBActions, set the dynamic cells class to that (rather than UITableViewCell) and drag your outlets/actions to _that_ rather than your table view controller subclass. (Yes, it's fiddly. See the last paragraph of my answer!) – Stephen Darlington Jul 20 '12 at 14:15
1

I would use dynamic prototype cells. Then, I would set up the ViewController as the delegate and the dataSource. I would then create a custom subclass of UITableViewCell and connect the elements of the second section to IBOutlets in the custom UITableViewCell.

If the first section wasn't something that could be done with one of the generic cell types, I would also create a custom subclass of UITableViewCell for that section as well.

I would then use the cellForRowAtIndexPath: method to set up the cells with the information that I want in them. So if my first section used FirstSectionCell and my second section used SecondSectionCell as custom subclasses of UITableViewCell my cellForRowAtIndexPath: would look like this:

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

    if(indexPath.section==0)
    {
        FirstSectionCell *firstCell = [tableView dequeueReusableCellWithIdentifier:@"First Cell Prototype"];

        //Set up the first cell.

        return firstCell;
    } 
    else if(indexPath.section ==1)
    {
        SecondSectionCell *secondCell = [tableView dequeueReusableCellWithIdentifier:@"Second Cell Ptototype"];

        //Set up second cell.
        secondCell.someLabel.text = @"whatever";
        //etc.

        return secondCell;
    }
    else 
    {
        //if you have another section handle it here.
    }
}
Justin Paulson
  • 4,388
  • 1
  • 23
  • 28
  • That's pretty much what I have now. I don't quite get how you connect the controls from the second section prototype cells to `IBOutlet`s, though. – LodeRunner Jul 20 '12 at 14:03
  • 1
    See this answer: http://stackoverflow.com/questions/10176312/connect-outlet-of-a-cell-prototype-in-a-storyboard/10176713#10176713 You can use `tag` or you can actually connect them up with interface builder. Connecting with interface builder is easier in the long run, if you ask me, it is the second answer, not the checked one that I wrote. – Justin Paulson Jul 20 '12 at 14:31
  • That means one `UITableViewCell` subclass for each different cell in the second section. And something to connect the row numbers to the actual subclass used. And something to fetch the control values from the cell subclasses when leaving the table view... Ugh. That's a lot of very specific code that won't fare well with any kind of refactoring. I was hoping I could avoid that. – LodeRunner Jul 20 '12 at 15:04
  • No, I think you are misinterpreting. You should not have control values in the cells that you need to fetch. Cells are just views. Your values should be saved in your model and the Cells should just be used to display those values. You are using reusable cells, so you don't need to connect each cell to a specific row, you just set up your `cellForRowAtIndexPath:` to handle configuring your reusable cell. You only need to create one prototype and link it to your subclass IBOutlets, then you can keep pulling a reusable version of that prototype and just set the IBOutlet properties. – Justin Paulson Jul 20 '12 at 15:47
  • I do understand that. However, I have five different layouts for the cells in the second section. The layouts are linked to different options the user can configure before starting a session. So each cell contains a textfield, or several, or a switch, etc. depending on the specific option to configure. I have to populate those `UIControl`s from the option defaults (so I need a subclass for each cell, and am table telling me which index path is linked to which cell subclass), and then, I need a way to pull the user values from the controls to start the session. – LodeRunner Jul 20 '12 at 16:00
  • Or you could write a method into the UITableViewCell subclass that does all of the configuring for you. You could have a method called "configureWithSetting:(NSDictionary *)settings" and have the UITableViewCell configure itself. Or you could use the different settings to select a different subclassed cell if you want to make subclasses for each option. Either way, if you want to be able to customize a bunch of stuff you are going to have to write code to do the customization, I don't understand how you think it should be made any easier? – Justin Paulson Jul 20 '12 at 16:03
  • As long as I didn't have a dynamic part in the table, I could use the storyboard and the outlets of my controller to do specific configuration. I figured there might be a way to have my table be half-static/half-dynamic and keep the easy connections from the Storyboard. Writing a specific subclass for each row seems tedious, seeing that it worked without the extra effort before I needed that dynamic first section. I'm even thinking it might be easier to just do section one statically, or add a Run Script build phase that would edit my storyboard XML for the first section. – LodeRunner Jul 20 '12 at 16:17
  • But I'm still working on a way to define my options layout in just one place (like an enum) and have the whole thing work dynamically from there. The only hang-up is the row-index-to-reuse-identifier table. But I think an `NSDictionary` will cover that. Thanks for all the help! – LodeRunner Jul 20 '12 at 16:21
  • I don't see how your static cells were allowing you to do any configuration that you can't do with dynamic cells. You still create easy connections from your storyboard to your subclass of `UITableViewCell` and then you just make your changes to the properties (outlets) of that cell rather than the properties (outlets) of the controller itself. – Justin Paulson Jul 20 '12 at 16:27
  • Static cells: control-to-`IBOutlet` connection in the controller. Explicitly named outlets for each control. Simple configuration from inside the controller code. Dynamic cells: control-to-`IBOutlet` connection in the `UITableViewCell` subclass. One subclass per row. Need to identify which subclass is used for which row inside `cellForRowAtIndexPath:`. More files, more code, more stuff to change if I decide to swap two rows or add an option, even though the second section is still perfectly static. There might be something I'm overlooking, though. – LodeRunner Jul 20 '12 at 16:38
  • whoa whoa, why do you need one subclass per row? Why do you need to identify which subclass is used for which row? You simply call `MyCustomCell *theCell = [tableView dequeueReusableCellWithIdentifier:@"Second Cell Ptototype"]` and then you configure it using the IBOutlets to whatever particular cell it was. In fact, this is less work than having to configure each IBOutlet to each cell in your view controller, because you only have to connect it once and only have to deal with one cell at a time. You can switch the `indexPath.row` to figure out which particular cell you are using and... – Justin Paulson Jul 20 '12 at 16:44
  • write the code for that cell in the switch. How is that any more difficult than writing a huge block of code that configures every single static cell? – Justin Paulson Jul 20 '12 at 16:44
  • Let me see if I understand. You're suggesting I write only one subclass of `UITableViewCell` that has all the `IBOutlet`s for every control I want to connect. But I only connect those that I need for a specific row? That could work. But that doesn't change that I need a row-to-identifier table that I didn't need when everything was connected through the Storyboard. And in the event of a layout change, I need this table to be robust enough that I don't find myself forgetting to update the table just for switching two rows. – LodeRunner Jul 20 '12 at 16:53
  • That's more complicated because connecting controls with explicit names in the controller is less error-prone than having to link row numbers to option types (i.e. cell types & reuse identifiers) somewhere in the code. – LodeRunner Jul 20 '12 at 16:55
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/14192/discussion-between-loderunner-and-justin-paulson) – LodeRunner Jul 20 '12 at 17:04
  • hey i was at lunch, i'm in that chat now if you want to pick the discussion back up – Justin Paulson Jul 20 '12 at 18:10