Creating custom cells in an iPhone TableView

Apr 20, 2011 13:50 · 772 words · 4 minute read iphone objective-C UITableView

The iPhone’s default table view is pretty straight-forward to use, but the look and feel is somewhat basic.  Once you’ve got to a certain point you need to customise things, which is where subclassing the cells comes into play.   It sounds horrifically complex, but once you’ve got your head around things, it’s not too difficult.  This is how I’ve done it for the WeWatch app (as an aide-memoire for myself, but it might come in useful for someone else.)

Each cell has a contentView, so customising the look and feel of a cell is a case of adding a subview to this contentView.  To do this, you’ve got two options:

  • creating a subview in code, then adding it

  • creating and laying out the custom cell visually in Interface Builder, then loading the resulting nib file whenever the table needs a new cell creating.

The two routes have the same end results, but if you’re creating a complex cell with lots of view objects, doing it in the visual designer is probably the easier approach.   The process goes like this:

One

Create a new view in which to create the new cell (File/New/New File/ and select the Empty File option from the templates).  You’ll need to give it a name - I’ve called it “baseCell”, but this can be anything to fit with your project’s conventions.

Two

From the Object Browser, select a Table View Cell and drag it out into the editor.  That gives you an empty cell of the default size.

You can then start customising the look and feel of the cell by dragging new objects into the Content View, until you’ve got it looking as you want it.

Three

The next step is to load the custom cell.  To do this, you’ll first need to create an outlet in the class that manages the table view (say for the sake of argument this is called ProgrammesViewController).  Open up the ProgrammesViewController.h file and add an outlet:

@interface ProgrammesViewController : UITableViewController {
...
// ivar to hold reference to a custom cell
UITableViewCell *nibLoadedCell;
...
}
...
// Property for the ivar
@property (nonatomic, retain) IBOutlet UITableViewCell *nibLoadedCell;
...
@end

Four

You’ll also need to @synthesize the ivar in the .m file.

Once you’ve done that, flip back to the cell in Interface Building and select the File’s Owner in the Placeholders section.  Then select the Identity inspect, and change the class of the File’s Owner to the table view’s controller - in this case, ProgrammesViewController.

Five

Now you can ctrl-click the File’s Owner icon in the Placeholder’s list, and connect the nibLoadedCell outlet to the Table View Cell icon in the Objects list below.  That’s connected the controller to the new view.

Six

Finally, select the Table View Cell itself, switch to the Attributes inspector and change the Identifier to the reuse identifier string that you previously set up in the cellForRowAtIndexPath method - by default, this is “cell” but in my code I’ve changed it to “ProgrammeCell”.

Seven

Then the final step is to tweak the block that creates a new cell if one isn’t available to be dequeued.  Normally this allocs and initWithStyles the cell - but in this scenario, we’re going to load the cell from the nib that we created.  This is done with

if (!cell) {
[[NSBundle mainBundle] loadNibNamed:@"BaseCell" owner:self options:NULL];
cell = nibLoadedCell;
}

There’s a bit of magic going on behind the scenes here.   The first line returns an NSArray with the contents of the nib, and because we’ve declared an outlet called nibLoadedCell, the table cell object in the array is automagically referenced by this.  So then it’s just a case of assigning it to the cell object, and this can then be used by the rest of the code as if we’d created a cell using the standard alloc/initWithStyle process.

Eight

To get to the objects in the custom cell, you’ll need to go and create tags for each of them in Interface Builder.  That’s midway down the Attributes inspector - I start tagging them at 1000 so it’s easier to insert tags subsequently.  So if we’ve got a UILabel with a tag of 1010, this can be referenced in the controller by

((UILabel *)[cell viewWithTag:1010])

What I’ve done is create a series of constants to make the code a bit more readable - so at the top of ProgrammesViewController I’ve got a series of lines that read

#define PROG_TITLE_LABEL ((UILabel *)[cell viewWithTag:1010])

and so on, while in the code I’ve then got

UILabel *titleLabel = PROG_TITLE_LABEL;
titleLabel.text = [p title];

which sets the content of the label with that tag.