When in the view lifecycle does UITableViewCell create its outlets/labels/subviews?

15,656

Generally speaking, objects should create their properties in their designated initializer, unless there is an overriding design principle (or a performance/resource issue) which mandates that they create them later.

The designated initializer for UIView (and NSView on OS X) is -initWithFrame:. The designated initializer for UITableView is initWithFrame:style:. Since a view will usually need to have it's visible subviews available immediately upon being added to its superview, it is fine to create and set them in the designated initializer.

-layoutSubviews is intended for updating the layout, which is to say the center, bounds (or frame) and (optionally) transform. Now, because of the ordering of messages. You don't want to create your subviews in -layoutSubviews because that method gets invoked repeatedly during the lifetime of your view, as its parent view's bounds changed and as it gets removed or re-added to its parent view, or as its subviews change.

In the case of UITableView, -layoutSubviews is called every time the table is reloaded.

A typical exception to this rule is the creation of UITableViewCells used as rows of the table, which must be created dynamically.

The -heightForRowAtIndexPath method provides a table view with the amount of space it needs to leave for that row's cell, but doesn't actually cause the cell to be resized. YOu have to set the cell's bounds yourself when creating the cell (or the cell can set its own bounds in -initWithStyle:reuseIdentifier:, if it's designed to be a fixed value). IF your cell's size does not match the table's expectations, you will get gaps or overlaps.

For autoresizing masks to work properly, you must configure the view's initial frame yourself (either in code or in a nib). Autoresizing a view affects how it responds to changes in its parent view's bounds, but does not help in determining the view's initial frame.

Specifically, you must set the initial frames for subviews of your cells (and also ensure sibling subview ordering, etc). This is easier to do in nibs than in code.

Share:
15,656
Awais Hussain
Author by

Awais Hussain

Updated on June 09, 2022

Comments

  • Awais Hussain
    Awais Hussain almost 2 years

    I am trying to create a UIView as a subview of a subclassed UITableViewCell. Essentially I want a view which is the same size as the cell, and sits between the cell's contentView and backgroundView.

    I imagine that somewhere under the hood (possibly in layoutSubviews), there is a line in UITableViewCell.m something like:

    if (self.contentView != nil) {
        [self addSubview:self.contentView];
    }
    

    If I want to mimic the way Apple does it, where should I put this code in my own custom UITableViewCell subclass?

    Also, in my first attempted implementation, the subview is displayed but it has the default cell height of 44px rather than the height I specified in tableView:heightForRowAtIndexPath:. There are also other small bugs that show up, which is why I'd like to try and replicate Apple's implementation rather than try my own semi-functional one.

    EDIT: Here's my code so far:

    In CustomTableViewCell.h

    interface CustomTableViewCell : UITableViewCell
        @property (nonatomic, strong) UIView *newSubview;
    @end
    

    In CustomTableViewCell.m

    - (void)layoutSubviews {
        [super layoutSubviews];
    
        if (self.newSubview != nil) {
            self.newSubview.autoresizingMask = UIViewAutoresizingFlexibleHeight;
            [self insertSubview:self.newSubview aboveSubview:self.backgroundView];
        }
    }
    

    In tableViewController.m

    static NSString *CellIdentifier = @"Cell";
    myCustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    
    if (cell == nil) {
        cell = [[CustomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }
    
    UIView *view = [[UIView alloc] initWithFrame:cell.frame];
    view.backgroundColor = [UIColor orangeColor];
    cell.newSubview = view;