iOS Recipes - Matt Drance [35]
It subclasses PRPNibBasedTableViewCell.
Its XIB filename matches the class name (PRPComplexTableViewCell.xib).
Its cell identifier matches the class name (PRPComplexTableViewCell).
The table cell is the first object in the nib.
Set Your Nib's Cell Identifier!
Whether or not you use this recipe, remember to set the Identifier attribute for every table view cell you create in Interface Builder. (See Figure 19, Reuse identifiers in Interface Builder .) This is a common oversight when using Interface Builder to create a table cell; you may fool yourself into thinking you’ve done it when you write the standard reuse code. You haven’t!
This careless mistake has huge performance implications. Always check your work by either setting a breakpoint or adding a log statement to your cell creation logic. You shouldn’t be instantiating any new cell objects after the initial burst when the table view is shown. If you’re instantiating cells indefinitely, the identifier set in your nib most likely does not match the identifier you’re using to produce the cell.
That last part is important. Taking a closer look at the implementation of +cellForTableView:fromNib:, we note that it throws an explanatory error if our nib contains something other than an instance of our cell subclass at the top.
We name the files and attributes accordingly to match the behavior inherited from PRPNibBasedTableViewCell. If we write our own subclass with a name that does not match the accompanying xib filename and/or cell identifier, no problem: we just override +cellIdentifier and +nibName in the subclass to return the appropriate strings.
Figure 19. Reuse identifiers in Interface Builder
* * *
As explained earlier, the calling code holds onto the generated nib. The SmarterTableCellsNib project includes a TestViewController class demonstrating how to do this.
Declare a property for the nib.
Create a lazy initializer to make the nib available on demand for any use case.
Clean up the property in ‑viewDidUnload and ‑dealloc.
SmarterTableCellsNib/Shared/TestViewController.h
@interface TestViewController : UITableViewController {}
@property (nonatomic, retain) UINib *complexCellNib;
@end
SmarterTableCellsNib/Shared/TestViewController.m
- (UINib *)complexCellNib {
if (complexCellNib == nil) {
self.complexCellNib = [PRPComplexTableViewCell nib];
}
return complexCellNib;
}
SmarterTableCellsNib/Shared/TestViewController.m
- (void)viewDidUnload {
[super viewDidUnload];
self.complexCellNib = nil;
}
- (void)dealloc {
[complexCellNib release], complexCellNib = nil;
[super dealloc];
}
Once we have a valid nib, we pass it to +cellForTableView:fromNib: from our ‑tableView:cellForRowAtIndexPath: method. The default implementation takes it from there.
SmarterTableCellsNib/Shared/TestViewController.m
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PRPComplexTableViewCell *cell =
[PRPComplexTableViewCell cellForTableView:tableView
fromNib:self.complexCellNib];
cell.titleLabel.text = [NSString stringWithFormat:@"Cell #%d",
indexPath.row];
cell.dateLabel.text =
[NSDateFormatter localizedStringFromDate:[NSDate date]
dateStyle:NSDateFormatterNoStyle
timeStyle:NSDateFormatterMediumStyle];
cell.locationLabel.text = @"Places unknown";
return cell;
}
And we’re done. With this recipe in place, you have two highly reusable techniques for rapid, clean use of custom table cells.
Recipe 17 Locate Table Cell Subviews
Problem
We all inevitably work on projects that require custom table view cell layouts. If these cells include controls or buttons, figuring out which row contains a given button can be difficult. How can you find an arbitrary button’s parent cell in a way that works for any layout or