28

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?

Community
  • 1
  • 1
Doug Smith
  • 29,668
  • 57
  • 204
  • 388

1 Answers1

45

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

mfaani
  • 33,269
  • 19
  • 164
  • 293
ilya n.
  • 18,398
  • 15
  • 71
  • 89
  • For the options where you want it *now* why do you have to call the `setNeeds...` method? Will the accompanying method not fire if the flag isn't set? And very often in auto layout related animations where you change constants [view layoutIfNeeded]` is called, but you say that's for frame. How does that work? If it's because auto layout does change the frame, isn't that the case in every use of auto layout that the frame is changed? – Doug Smith Nov 30 '13 at 22:32
  • *Will the accompanying method not fire if the flag isn't set?* – exactly so. – ilya n. Nov 30 '13 at 23:17
  • *that's for frame.* – the role of this method is to set the layout, and setting a frame directly is the *simplest possible* implementation, but it's indeed more complicated for auto layout. – ilya n. Nov 30 '13 at 23:20
  • *How does that work?* Animations are a different topic altogether. If you animate constraint, my guess is the constraint doesn't "change" in the sense that `[view setNeedsUpdateConstraints]` is not called. – ilya n. Nov 30 '13 at 23:21
  • I like it when people ask good questions (as in http://stackoverflow.com/questions/how-to-ask). Makes it more fun to answer. – ilya n. Dec 01 '13 at 10:54
  • Call all 4 methods and finally, I can get the view's correct frame immediately. – yong ho Jul 25 '17 at 04:17
  • 1. You don't know how many times I've came back to your answer...Q: " system will perform calls to `[view updateConstraintsIfNeeded]` at each layout pass" by each layout pass you mean something like: `topAnchor, constant: 23),titleField.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -18).isActive = true`? 2. Your link for Auto Layout is dead...imho it seems is a bit unnecessary here – mfaani Dec 14 '17 at 20:32
  • I'm confused about the manner of updating constraints and based on your answer I wrote [this](https://stackoverflow.com/questions/47823639/why-calling-setneedsupdateconstraints-isnt-needed-for-constraint-changes-or-ani). Can you please take a look? – mfaani Dec 15 '17 at 20:36