Loading UITableViewCells from a nib file

While working on iPhone applications I have found it useful to load as much of the UI layout and styling from nib files as possible. As a result I often want to load the cells for a table view from a nib file and created a factory class to handle this behavior every time I want to reuse it.
When I need to use a custom cell in a table view I add a cell factory as a property on the view’s controller and select a cell to use by setting that property when constructing the controller. This often allows me to reuse a single factory across multiple controllers so I don’t have to repeat myself by including duplicate references to the same cell nib in every controller.

@interface CellFactory : NSObject {
	Class cellClass;
	NSString *nibName;
	NSString *identifier;
}
 
+ (CellFactory *) factoryWithClass:(Class) theCellClass andNib:(NSString *)theNibName andIdentifier:(NSString *)theIdentifier;
 
@property(nonatomic, retain) Class cellClass;
@property(nonatomic, retain) NSString *nibName;
@property(nonatomic, retain) NSString *identifier;
 
- (UITableViewCell *) createCell;
- (UITableViewCell *) createCellWithOwner:(id)owner;
 
@end
 
@implementation CellFactory
 
@synthesize cellClass, nibName, identifier;
 
+ (CellFactory *) factoryWithClass:(Class) theCellClass andNib:(NSString *)theNibName andIdentifier:(NSString *)theIdentifier {
	CellFactory *factory = [[[CellFactory alloc] init] autorelease];
	factory.cellClass = theCellClass;
	factory.nibName = theNibName;
	factory.identifier = theIdentifier;
	return factory;
}
 
- (UITableViewCell *) createCellWithOwner:(id)owner {
	NSArray* nibContents = [[NSBundle mainBundle] loadNibNamed:self.nibName owner:owner options:nil];
    for (id nibItem in nibContents) {
        if ([nibItem isKindOfClass:self.cellClass] && (self.identifier == nil || [[(UITableViewCell *)nibItem reuseIdentifier] isEqualToString: self.identifier])) {
            return (UITableViewCell *) nibItem;
        }
    }
    return nil;
}
 
- (UITableViewCell *) createCell {
    return [self createCellWithOwner:self];
}
 
@end
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
	UITableViewCell *cell;
	cell = [self.tableView dequeueReusableCellWithIdentifier:self.cellFactory.identifier];
	if (cell == nil) {
		cell = [self.cellFactory createCell];
	}
	return cell;
}

An example app using this factory is available in the Carbon Five svn repository: http://svn.carbonfive.com/public/jonah/CellFactoryExample

Share:
  • Digg
  • del.icio.us
  • Facebook
  • Technorati
  • StumbleUpon
  • TwitThis
  • email

1 Responses to “Loading UITableViewCells from a nib file”


  • Looks like there is a memory leak from reading the code.
    All three instance vars use a retain property attribute.
    Need a dealloc method that sets them to nil. e.g.,

    - (void) dealloc
    {
    self.cellClass = nil;
    self.theNibName = nil;
    self.identifier = nil;
    [super dealloc];
    }

    One might argue that the strings being passed in should instead use a “copy” property attribute since they may be strings dynamically constructed in a way that makes them mutable and thus subject to change after being passed into this method/class.

    Further, this is a style thing but it might be considered more standard and would also allow for creation via alloc/init if it were written this way:

    + (CellFactory *) factoryWithClass:(Class) theCellClass andNib:(NSString *)theNibName andIdentifier:(NSString *)theIdentifier
    {
    CellFactory *factory = [[[CellFactory alloc] initWithCellClass: theCellClass
    andNibName: theNibName
    andIdentifier: theIdentifier] autorelease];
    return factory;
    }

    and then add the designated initializer to the .h file

    // designated initializer
    - (id) initWithCellClass: (Class) theCellClass andNibName: (NSString*) theNibName andIdentifier: (NSString*) theIdentifier;

    and the .m file:

    - (id) initWithCellClass: (Class) theCellClass andNibName: (NSString*) theNibName andIdentifier: (NSString*) theIdentifier
    {
    self = [super init];
    if (self )
    {
    self.cellClass = theCellClass;
    self.nibName = theNibName;
    self.identifier = theIdentifier;
    }
    return self;
    }

    The other change I’d make is to rename the class method to have “new” in it’s name. The caller is nearly always going to want to keep the created instance around, so make a class method that doesn’t autorelease the return value and require the caller to call retain on it. Change the name so that it follows the cocoa object ownership conventions; something like: newCellFactoryWithClass would be appropriate, though newCellFactortyForCellClass might catch one’s fancy.

    Clearly not a big deal, just a different style/approach to such things.
    Thanks for contributing the starting point for this. It’s a good idea.

Leave a Reply