uitableviewcell + model object
DESCRIPTION
Super brief talk I gave about the various approaches for configuring a UITableViewCell with a model object. I gave this talk at the March Cocoaheads meeting in 2013. More information about the meeting below. http://www.brynbodayle.com/nashville-cocoaheads-recap-321/TRANSCRIPT
UITableViewCell + Model Object
Bryn BodayleiOS Developer
Three Ways?
1. View controller con!guring
2. Pass model to cell
3. Categories
-“Default” method
-Maintains strict MVC
-Bad reuse: copy & paste
-Easy but cluttered
-Dynamic cell heights clunky
-Goodbye skinny view controllers
-Lots of constants
1. View controller con!guring
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ NSString *reuseIdentifier = @"Tweet Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier]; if (nil == cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier]; cell.textLabel.font = [UIFont systemFontOfSize:14]; cell.textLabel.numberOfLines = 0; cell.textLabel.backgroundColor = [UIColor clearColor]; cell.contentView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"listbg.png"]]; } cell.textLabel.text = [[_statuses objectAtIndex:indexPath.row] text]; return cell;}
1. View controller con!guring
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{! static NSString *CellIdentifier = @"CustomTableViewCell";
! CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];! if (cell == nil)! ! cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
ModelObject *object = self.data[indexPath.section][indexPath.row]; NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc] initWithString:object.title]; [attributedTitle setAttributes:@{UITextAttributeFont : [UIFont fontWithName:@"Comic Sans" size:20]} range:NSMakeRange(0, 10)]; [attributedTitle setAttributes:@{UITextAttributeFont : [UIFont fontWithName:@"Zapfino" size:20]} range:NSMakeRange(10, 20)]; [cell.titleText setAttributedText:attributedTitle]; [cell.titleText sizeToFit];
cell.detailText.text = object.detail; [cell.titleText sizeToFit]; cell.profileImage.image = object.image;
[UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^(void) { cell.profileImage.alpha = 1; } completion:^(BOOL finished) { }]; ! return cell;}
1. View controller con!guring
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { ModelObject *object = self.data[indexPath.section][indexPath.row]; if(object.image) {
CGSize size = [object.detail sizeWithFont:DETAIL_FONT constrainedToSize:CGSizeMake(DETAIL_WIDTH, MAXFLOAT) lineBreakMode:DETAIL_LINE_BREAK_MODE]; return MAX(size.height + CELL_PADDING, 70); } else { CGSize size = [object.detail sizeWithFont:DETAIL_FONT_SMALL constrainedToSize:CGSizeMake(DETAIL_WIDTH_IMAGE, MAXFLOAT) lineBreakMode:DETAIL_LINE_BREAK_MODE]; return MAX(size.height + CELL_PADDING, 50);
}}
1. View controller con!guring
-Quick & easy
-Easy dynamic heights
-Tightly couples cell to model object
-Clean interface
-Nimbus Framework
-Deadly sin?
2. Pass model to cell
When a UITableViewCell subclass accepts a model object parameter and updates its constituent subviews as I have described, it is behaving as a data transformer, not a controller. It does not care about any future updates to the model unless the controller tells it to transform the updated model object, or to transform a completely different model instance.
- Paul Goracke
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{! static NSString *CellIdentifier = @"CustomTableViewCell";
! CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];! if (cell == nil)! ! cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
! ModelObject *object = self.data[indexPath.section][indexPath.row]; [cell updateCellWithObject:object]; ! return cell;}
2. Pass model to cell
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
ModelObject *object = self.data[indexPath.section][indexPath.row]; return [CustomTableViewCell cellHeightForDetailText:object.detail];
2. Pass model to cell
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { ModelObject *object = self.data[indexPath.section][indexPath.row]; return [CustomTableViewCell cellHeightForObject:object];}
-Middle ground
-Loose coupling
-Extra !les
-Subviews must be public
-Multiple model objects for one cell
3. Categories
#import "FAScoreboardCell.h"
@class Athlete;
@interface FAScoreboardCell (Athlete)
+ (NSInteger)heightForText:(NSString *)text;- (void)updateWithAthlete:(Athlete *)athlete;
@end
3. Categories
-Brent Simmons: “UITableViewCell Is Not a Controller”
-http://inessential.com/2012/12/31/uitableviewcell_is_not_a_controller
-Paul Goracke: “UITableViewCell Is Not a Controller, But...”
-http://corporationunknown.com/blog/2013/01/01/uitableviewcell-is-not-a-controller-but/
-Sebastian Rehnby: “Skinnier Controllers Using View Categories”
-http://www.sebastianrehnby.com/blog/2013/01/01/skinnier-controllers-using-view-categories/
Further Reading
Questions?