iOS Recipes - Matt Drance [34]
cellForTableView:tableView];
cell.mainLabel.text = [self.quotes objectAtIndex:indexPath.row];
return cell;
}
The controller code is significantly reduced and much more readable—it now contains only the customization of the cell for that particular view. All the cell’s characteristic logic and layout is hidden away in the cell class, allowing it to be easily reused anywhere else in this or another project. If you were planning to write a UITableViewCell subclass, this additional code could save you a lot of work in the long run. If you’re writing a basic table view with one of the standard cell types, it could be overkill.
This pattern pays especially large dividends when you’re writing a heavily customized table view with assorted types of cells. We’ll explore this further in Recipe 18, Organize Complex Table Views .
You can also easily extend this pattern to use custom cells created in Interface Builder, as you’ll see in the next recipe.
Recipe 16 Use Smart Table Cells in a Nib
Problem
The previous recipe, Recipe 15, Simplify Table Cell Production , showed you how to create a complex custom table cell with ease while significantly reducing the amount of controller code you have to write. What if you prefer to create your cells in Interface Builder?
Solution
The “smart table cell” pattern we just explored is easily adaptable to nib-based cells. As is usually the case when using Interface Builder (IB), we end up saving even more code than before. We’ll apply the same core principle of abstracting the reuse labor away from the controller, but the controller does need to contribute a little more than it did last time. Specifically, we’ll ask the controller to manage the nib.
Our PRPNibBasedTableViewCell seems very familiar if you’ve reviewed PRPSmartTableViewCell: it has a +cellIdentifier method that returns a custom reuse identifier, and it has a convenience method for the typical dequeue-or-instantiate dance we do for every table cell we create.
SmarterTableCellsNib/Shared/PRPNibBasedTableViewCell.m
+ (NSString *)cellIdentifier {
return NSStringFromClass([self class]);
}
SmarterTableCellsNib/Shared/PRPNibBasedTableViewCell.m
+ (id)cellForTableView:(UITableView *)tableView fromNib:(UINib *)nib {
NSString *cellID = [self cellIdentifier];
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:cellID];
if (cell == nil) {
NSArray *nibObjects = [nib instantiateWithOwner:nil options:nil];
NSAssert2(([nibObjects count] > 0) &&
[[nibObjects objectAtIndex:0] isKindOfClass:[self class]],
@"Nib '%@' does not appear to contain a valid %@",
[self nibName], NSStringFromClass([self class]));
cell = [nibObjects objectAtIndex:0];
}
return cell;
}
Note the generation method is a little different here: it takes a second parameter for a UINib object. UINib is a new class designed to minimize overhead when rapidly instantiating views from a nib file. Rather than calling -[NSBundle loadNibNamed:owner:options:], we hold onto our UINib object and call ‑instantiateWithOwner:options: to get a fresh copy of our nib objects.
In this recipe, we ask the calling code—presumably a UITableViewDataSource—to hold onto that nib, but we still make it easy as possible to get one. The +nib and +nibName methods provide easy access to the nib that hosts our custom cell.
SmarterTableCellsNib/Shared/PRPNibBasedTableViewCell.m
+ (UINib *)nib {
NSBundle *classBundle = [NSBundle bundleForClass:[self class]];
return [UINib nibWithNibName:[self nibName] bundle:classBundle];
}
+ (NSString *)nibName {
return [self cellIdentifier];
}
The methods are straightforward enough: +nib looks in the class’s bundle, using the name returned by +nibName. By default, +nibName relies on +cellIdentifier, which defaults to the classname. This default behavior should scale to any number of subclasses, as long as we configure our files accordingly.
Let’s take a look at this pattern in practice. Open the SmarterTableCellsNib project and navigate to the PRPComplexTableViewCell class. This class inherits