bdunagan

Brian Dunagan

June 28 2009
Custom UITableViewCell from a XIB in Interface Builder

Looking around the App Store, I see most apps customize their UITableViews in a unique way. Flixster embeds movie posters and ratings, in addition to their titles. Tweetie integrates tweets, icons, usernames, and the date. GasBuddy lists service type, amount spent, gallons, and dollars per gallon in each row. Constructing these customized UITableViewCells is possible in code, but leveraging Interface Builder's drag-and-drop interface is far more fun. Thanks to Bill Dudney for talking about one approach to this on his blog and to StackOverflow for covering this topic.

Creating a custom UITableViewCell using Interface Builder is straight-forward.

  1. In Xcode, create a new UITableViewCell subclass and add the desired IBOutlets to the header file.
  2. In Interface Builder, create an "Empty" XIB from the Cocoa Touch palette.
  3. Drag a UITableViewCell from the Library into it, configure the class to be your new custom UITableViewCell subclass, and give it the appropriate identifier.
  4. Add the desired elements to the UITableViewCell and connect the subclass's outlets to them.
  5. To instantiate the cells using the UIViewController approach, set class of the new XIB's "File's Owner" to be "UIViewController", and connect its view outlet to the customized UITableViewCell. </ol>

    The XIB is set up, but there are two approaches to instantiating the new customized cell from that XIB. When I was at WWDC a couple weeks ago, I confirmed with one of the Interface Builder engineers at the IB Lab that both work just fine. (He did repeatedly ask if I was using UITableView:dequeueReusableCellWithIdentifier:, just to make sure.)

    UIViewController

    One approach is to create a temporary UIViewController each time you need a new cell. By setting up the XIB the way we did, the temporary UIViewController has the cell as its view attribute. After we grab a pointer to that, we can release the view controller.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
        if (cell == nil) {
    		// Create a temporary UIViewController to instantiate the custom cell.
            UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
    		// Grab a pointer to the custom cell.
            cell = (BDCustomCell *)temporaryController.view;
    		// Release the temporary UIViewController.
            [temporaryController release];
        }
    
        return cell;
    }

    NSBundle:loadNibNamed:owner:options:

    Another approach is to load the NIB file and grab the cell directly, as it's the only top-level object in the NIB.

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
        if (cell == nil) {
    		// Load the top-level objects from the custom cell XIB.
            NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
    		// Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
    		cell = [topLevelObjects objectAtIndex:0];
        }
    	
        return cell;
    }
ibtool Caveats ibtool scripts on Google Code
LinkedIn GitHub Email