Joris Kluivers

I like to build software.

NIB Based Table View Cells

With the introduction of Storyboards for iOS and view based table views on OS X it is now possible to fully design table views in Interface Builder. Tables hold one or more prototype views which are returned by your delegate method based on a given identifier.

However designing these prototype views is less than ideal. Editing a small area nested within a table view easily leads to mistakes (and broken auto layout constraints).

Thats why I prefer to have each of my cells in individual NIB files. With the added advantage of being able to reuse the cells between different tables. In this post I take a look at the ways to load table view cells from NIB files, on both OS X and iOS.

Recycling rows

Tables in both UIKit and AppKit (as of 10.7) use views for each individual row/cell. To keep memory footprint low and scrolling smooth only as much views as needed to fill the screen are used. Rows that scroll off screen are recycled and displayed as new content.

See Framer: TableView Recycle for a nice animated demonstration of this technique.

Each type of row is uniquely identified by an identifier. In your delegate you ask your table for a cell from the reuse queue. If none available the table will create a new one for you.

1
2
3
4
5
6
- (UITableviewCell *) tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
  UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:@"SomeIdentifier"];
  // configure cell
  return cell;
}

Registering external NIBs with table views

The identifier to dequeue a cell refers to the identifier you specified for the cell in your NIB or Storyboard. In a Storyboard you select the prototype cell and provide an identifier string in it’s inspector. This works similar in a NSTableView on a Mac.

Designing prototype cells in Interface Builder can only be done in the same table where they will be used. And on iOS this unfortunately only works in Storyboards, not in tableviews in XIB files.

However it’s possible to let your table know about cells available in other XIB files. Both iOS and OS X use very similar methods for this.

NSTableView 10.8 and up
1
2
NSNib *cellNib = [[NSNib alloc] initWithNibNamed:@"MyCell" bundle:nil];
[myTable registerNib:cellNib forIdentifier:@"SomeIdentifier"];
UITableView iOS 5 and up
1
2
UINib *cellNib = [UINib nibWithNibName:@"MyCell" bundle:nil];
[myTable registerNib:cellNib forCellReuseIdentifier:@"SomeIdentifier"];

First you instantiate a nib file from the default bundle. Next that nib instance is registered with the table and associated a reuse identifier. The next time you ask the table for a cell with that identifier it will either be instantiated from the nib, or you’ll get a recycled one if available.

Using these methods it’s now possible to keep all your cells in separate nib files on both platforms.

Older OS support

If you need your software to work on older OS versions it is still possible to use the same NIB files. While the table view won’t load the NIB files automatically it is not hard to load them manually using UINib or NSNib.

For example I’ve been using a NSTableCellView category to instantiate from a NIB file:

NSTableCellView+JKNibLoading.h Download
1
+[NSTableCellView tableCellViewWithNibNamed:owner:]

Example

In the recent Cocoa Configurations demo I published I use this technique in a NSOutlineView, where each row is a different inspector panel loaded from a separate NIB file.

Check the source for JKMainWindowController.m which eventually loads the FileInspector.xib as one of its rows.