Customize that UIViewCell – Part 1: Using Interface Builder

If you followed my first tutorial on UITableView (linksource code), you now have a very simple app that shows a list of DVD titles and clicking any of the titles shows you a detail view page with more information about the DVD.  That’s all nice but we really want to make it a little bit prettier. We could display the length of each movie right on the listing page. Also, we have this coverImage field in our data set, let’s use it.

What we want is for our home screen to look like this:

tutorial006

We can accomplish that by customizing UITableViewCell. There are 2 ways of going about it:

  1. Using Interface Builder
  2. Programmatically

In reality, you can also choose a hybrid approach where you create some UI elements in the Interface Builder and some programmatically.

Which option to choose?

The two approaches both bring some advantages and disadvantages with them.

Interface Builder

If you decide to go the Interface Builder route, you’ll find it very easy to create and customize your cells. Any subsequent edits can also be done quite easily since you’re simply rearranging elements visually. The downside is speed and performance since the system needs to render each view in a cell individually. If your table view has thousands of rows in it, this may/may not affect the performance of your app, depending how complicated your cell is.

Programmatically

This one involves a lot more work. You are responsible for creating each UI element by hand in the code. That can be very tedious and any edits you need to make in the future require code changes. Also, you’ll need to set up all the autosizing masks yourself. The upside is performance. Since the system will draw each cell as one view, the performance gain can be very significant.

Ok, let’s get to it…

1. Subclass UITableViewCell

Create a Cocoa Touch Class file and make it a subclass of UITableViewCell. Let’s name it DVDListingViewCell.m. We’ll use this class as a placeholder for our labels and the cover image. In DVDListingViewCell.h add the following code:

@interface DVDListingViewCell : UITableViewCell {
    IBOutlet UILabel *titleLabel;
    IBOutlet UIImageView *coverImageView;
    IBOutlet UILabel *featureLengthLabel;
}
 
@property (nonatomic, retain) UILabel *titleLabel;
@property (nonatomic, retain) UILabel *featureLengthLabel;
@property (nonatomic, retain) UIImageView *coverImageView;
 
@end

We use the IBOutlet annotation here to later let Interface Builder know we want to connect these properties with the actual UI elements we’ll lay out.

We also need synthesize the properties we’ve just defined. This will create all setters and getters for us. Add this to DVDListingViewCell.m right below the @implementation directive.

@synthesize titleLabel, featureLengthLabel, coverImageView;

2. Design the cell in Interface Builder

In Xcode, double-click on any of the nib files (they should all be listed under NIB Files magic folder). Once in Interface Builder, choose New… from the File menu. You should be presented a dialog with several templates in it. From the Cocoa Touch category on the left, choose Empty.

tutorial007

You should now see an “Untitled” window with File’s Owner and First Responder in it. One thing missing right now is the actual cell view, let’s add it. From the Library window (Tools -> Library) locate Table View Cell. Drag it to the “Untitled” window. You should now see something like this:

tutorial008

Double clicking on the Table View Cell object will open up the actual cell view where we’re going to add our UI elements. Make the cell 120 pixels in height (you change the dimensions under the size tab in the properties window or by pressing Command  + 3). Drag a couple Lables and an Image View to the cell to lay it out as shown below. Once done, save the file as DVDListingView. Make sure to save the file in your project directory and check the checkbox when prompted if you want to add the file into your project.

tutorial009

3. Prepare the RootViewController

Return back to Xcode and open up your RootViewController.h header file. We will add a cell instance variable of type DVDListingViewCell that we’ve created earlier. We will use this cell to actually draw on the screen. Modify your header file to make it look like this:

@interface RootViewController : UITableViewController {
    DvdLibraryDao *dao;
 
    IBOutlet DVDListingViewCell *_cell;
}

You may notice the IBOutlet annotation again. That is because we will be connecting this variable to the actual cell we created in Interface Builder.

Let’s switch to the implementation file of our RootViewController (RootViewController.m). You may remember the method cellForRowAtIndexPath from the first tutorial. That’s the method that controls what cell is drawn at which row. We’ll want our cell to be a DVDListingViewCell so let’s modify the current version of it to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"LibraryListingCell";
 
    DVDListingViewCell *cell = (DVDListingViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"DVDListingView" owner:self options:nil];
        cell = [_cell autorelease];
        _cell = nil;
    }
 
    cell.titleLabel.text = [[dao libraryItemAtIndex:indexPath.row] valueForKey:@"title"];
    cell.featureLengthLabel.text = [NSString stringWithFormat:@"%@ minutes",
                                    [[dao libraryItemAtIndex:indexPath.row] valueForKey:@"featureLength"]];
    cell.coverImageView.image = [UIImage
                                 imageNamed:[[dao libraryItemAtIndex:indexPath.row] valueForKey:@"coverImage"]];
 
    return cell;
}

There are few things going on so let’s analyze them line by line.

Line #5 was changed to initialize the cell local variable to the DVDListingViewCell instead of the generic UITableViewCell.

Lines #6-#10 contain the meat of the cell creation. It first loads the nib file we created. You can see we’re setting the owner to self. That is because the _cell instance variable will get initialized via the nib file, which I’m going to cover a little later.

And finally, Lines #12-#15 assign proper values to the labels and image view in our custom cell.

4. Connect everything in Interface Builder

Return back to Interface Builder and click on the File’s Owner icon. Open up the Identity Inspector (Tools -> Identity Inspector) and look at the Class drop-down menu. Locate RootViewController and select it. In the same window, you should now see the _cell instance variable that belongs to RootViewController. When the nib file is loaded, we’ll want the cell we designed in Interface Builder to be assigned to our _cell. Let’s do that next.

Open up Connections Inspector (Tools -> Connections Inspector) with File’s Owner icon still selected. You should see our _cell in the Outlets section. Next to it is a little hollow circle that turns into a plus (+) sign when you hover it. Click on the circle and drag over to the Listing View Cell window. When you drag over it, the cell should highlight with a label that says “Listing View Cell.” Release the mouse to complete the connection (the cell will blink).

tutorial010

Note: There is an alternate way of creating the connection; Click on the File Owner’s icon while pressing down the Control key and Ctrl-drag to the Listing View Cell icon. A little black pop-up window “Outlets” will appear. Select _cell and let go of the mouse.

tutorial011

Now let’s connect our two labels and the image view. Highlight the Listing View Icon and open up Identity Inspector (Command+4). Under the Class drop-down find and select our DVDListingViewCell. Once done, you should see all the outlets we defined earlier in the DVDListingViewCell class: coverImageView, featureLengthLabel and titleLabel. Open up Connections Inspector (Command+2) and check out the Outlets section. Just as before, drag a connection from the hollow circle next to titleLabel to the “My DVD” label, featureLengthLabel to the feature length label and finally coverImageView to the UIImageView object in our Listing View Cell.

tutorial012

Alternatively, you can Control-drag from the Listing View Cell icon to the Listing View Cell window and connect the three objects that way.

tutorial013Save all your changes and return back to Xcode.

5. Set the cell height in RootViewController

After you’ve saved your changes in Interface Builder and return back to Xcode, go ahead and run the project. You’ll see that everything works well except one thing – the cells are all crammed together.

tutorial014

This is because we need to explicitly set the height of cells in each row when it differs from the default. We can correct that by implementing the heightForRowAtIndexPath delegate method in the RootViewController.m file.

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 120.0;
}

Rerun your project and you should now see the cells correctly sized and looking good.

tutorial006

Conclusion

Remember that this is just one of a couple ways to customize your cells. You can do all of this by hand, programmatically which is more work but may result in a better performance. We will cover that in the second part of this tutorial.

You can download the complete source code to this tutorial here: My DVD Library Xcode Project 02.


14 Responses to “Customize that UIViewCell – Part 1: Using Interface Builder”

Copyright © 2009 Vladimir Olexa | Copyright © 2009 Apple Inc. | Powered by WordPress