Episode #50

Custom Cells

26 minutes
Published on January 24, 2013

This video is only available to subscribers. Get access to this video and 572 others.

In this episode we design a custom table view cell including a designing custom repeatable background & highlight images in Photoshop, dynamically layout out the labels, as well as observing a tricky problem regarding cell animations. We also take a look at how to profile the graphics performance (FPS) using Instruments.

Episode Links

Creating the Custom Cell

@interface CustomCell : UITableViewCell

@property (nonatomic, strong) UILabel *mainTextLabel;
@property (nonatomic, strong) UILabel *subTextLabel;

@end

Note that we override the labels so we have full control over them and nothing is done to manipulate them by the superclass.

@implementation CustomCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        [self addMainLabel];
        [self addSubLabel];
        [self setupBackground];
    }
    return self;
}
...
@end

Next, setting up the background & labels...

- (void)setupBackground {
    UIImage *bg = [UIImage imageNamed:@"cell-bg.png"];
    UIImageView *backgroundView = [[UIImageView alloc] initWithImage:bg];
    self.backgroundView = backgroundView;

    UIImage *highlight = [UIImage imageNamed:@"cell-bg-highlighted.png"];
    UIImageView *highlightImageView = [[UIImageView alloc] initWithImage:highlight];
    self.selectedBackgroundView = highlightImageView;
}

- (void)addMainLabel {
    self.mainTextLabel = [[UILabel alloc] init];
    self.mainTextLabel.font = [UIFont boldSystemFontOfSize:16];
    self.mainTextLabel.textColor = [UIColor colorWithWhite:0.45 alpha:1.0];
    self.mainTextLabel.shadowColor = [UIColor whiteColor];
    self.mainTextLabel.shadowOffset = CGSizeMake(0, 1);
    self.mainTextLabel.backgroundColor = [UIColor clearColor];
    self.mainTextLabel.highlightedTextColor = [UIColor whiteColor];

    [self.contentView addSubview:self.mainTextLabel];
}

- (void)addSubLabel {
    self.subTextLabel = [[UILabel alloc] init];
    self.subTextLabel.font = [UIFont systemFontOfSize:14];
    self.subTextLabel.textColor = [UIColor colorWithWhite:0.55 alpha:1.0];
    self.subTextLabel.shadowColor = [UIColor whiteColor];
    self.subTextLabel.shadowOffset = CGSizeMake(0, 1);
    self.subTextLabel.backgroundColor = [UIColor clearColor];
    self.subTextLabel.highlightedTextColor = [UIColor whiteColor];

    [self.contentView addSubview:self.subTextLabel];
}

Dynamically laying out the labels

We need to make sure that we set the label frames in the layoutSubviews method. Here we calculate the frames using variables so that everything is calculated off of known values (such as text width and margins). This is not possible in Interface Builder without using Autolayout, so we do it in code here.

- (void)layoutSubviews {
    [super layoutSubviews];

    const CGFloat HorizontalMargin = 10;
    const CGFloat VerticalMargin = 4;

    CGFloat x = HorizontalMargin;
    CGFloat y = VerticalMargin;
    CGSize textSize = [self.mainTextLabel.text sizeWithFont:self.mainTextLabel.font];
    CGFloat width = textSize.width;
    CGFloat height = self.contentView.frame.size.height - (2 * VerticalMargin);
    self.mainTextLabel.frame = CGRectMake(x, y, width, height);

    x += width + HorizontalMargin;
    textSize = [self.subTextLabel.text sizeWithFont:self.subTextLabel.font];
    width = textSize.width;
    self.subTextLabel.frame = CGRectMake(x, y, width, height);
}

Fixing the selection shadow problem

The selection style of the labels indicates a white font, which doesn't work well with the white shadow color. For the selection state, we'll just remove the shadow.

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
}

- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
    [super setHighlighted:highlighted animated:animated];

    CGSize shadowOffset = highlighted ? CGSizeZero : CGSizeMake(0, 1);
    self.mainTextLabel.shadowOffset = shadowOffset;
    self.subTextLabel.shadowOffset = shadowOffset;
}