What is the difference between all these Auto Layout update methods? Are all necessary?

Doug Smith picture Doug Smith · Nov 30, 2013 · Viewed 9.6k times · Source

In the code below, these four methods are called for layout reasoning. I'm a little confused why all of them are needed, though, and what they do differently from one another. They're used in the process to make a cell's height be dynamic with Auto Layout. (Taken from this repository from this question.)

[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];

And it's from this block of code for the cell's height:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{

    RJTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    [cell updateFonts];

    NSDictionary *dataSourceItem = [self.model.dataSource objectAtIndex:indexPath.row];
    cell.titleLabel.text =  [dataSourceItem valueForKey:@"title"];
    cell.bodyLabel.text = [dataSourceItem valueForKey:@"body"];

    cell.bodyLabel.preferredMaxLayoutWidth = tableView.bounds.size.width - (kLabelHorizontalInsets * 2.0f);

    [cell setNeedsUpdateConstraints];
    [cell updateConstraintsIfNeeded];
    [cell.contentView setNeedsLayout];
    [cell.contentView layoutIfNeeded];

    CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;

    return height;
}

But what are they doing differently? Why are they all needed?

Answer

ilya n. picture ilya n. · Nov 30, 2013

Layout

Suppose you encapsulate your view logic in a UIView subclass, and call it SomeView. This means that SomeView should know how to layout itself, that is, how to position some other views inside it (you can also create a view that draws itself without using any subviews, but that's beyond the needs of an average developer).

This layout is done by [SomeView layoutSubviews]. You have an option of overriding it:

subview.frame = CGRectMake(100 + shiftX, 50 + shiftY, 250 + shiftX - paddingRight, ...
// Oh no, I think I didn't do it right.

but you rarely need to do so. In the dark ages of Cocoa Touch, this manual layout was widespread, but now I'd say 99% of layouts can be covered with Auto Layout.

The system needs to know when it should call [UIView layoutSubviews]. It's obviously done the first time you need to draw a view, but it may be also called whenever the superview frame changes. Here's a detailed explanation.

So the system often calls [view layoutIfNeeded]. You can also call it at any time, but this will have an effect only if there is some event that has called [view setNeedsLayout] or if you have called it manually, as in this case.

Constraints

The Auto Layout (capitalized this way in the documentation) is called like that because you're leaving [SomeView layoutSubviews] as it is inherited from UIView and describe the position of your subviews instead in terms of constraints.

When using Auto Layout, system will perform calls to [view updateConstraintsIfNeeded] at each layout pass. However, only if the flag [view setNeedsUpdateConstraints]; is set, the method calls into -updateConstraints (which does the real job).

If you don't use Auto Layout, those methods are not relevant.

You can implement it like in this example.

Your example

It's rarely necessary to call -layoutIfNeeded and -updateConstraintsIfNeeded directly, because UI engine will do that automatically at each layout pass. However, in this case the author has chosen to call them immediately; this is because the resulting height is needed right now, not at some point in the future.

This method of updating the cell's height seems right. Note that cell could be a newly created cell and thus not added into view hierarchy yet; this does not impact its ability to layout itself.

Conclusion

In your custom view go with the following options, starting from the most 'universal' to most 'customized':

  1. Create constraints during view creation (manually or in the IB)
  2. If you need to change the constraints later, override -updateConstraints.
  3. If have a complex layout that cannot be described by above means, override -layoutSubviews.

In the code that changes something that could make your view's constraints change, call

[view setNeedsUpdateConstraints];

If you need results immediately, call also

[view updateConstraintsIfNeeded]; 

If the code changes view's frame use

[view setNeedsLayout]; 

and finally if you want the results immediately, call

[view layoutIfNeeded];

This is why all four calls are required in this case.

Additional materials

Take a look at detailed explanation in the article Advanced Auto Layout Toolbox, objc.io issue #3