ChatGPT解决这个技术问题 Extra ChatGPT

setNeedsLayout vs. setNeedsUpdateConstraints and layoutIfNeeded vs updateConstraintsIfNeeded

I know that the auto layout chain consists in basically 3 different process.

updating constraints layout views (here is where we get calculation of frames) display

What's is not totally clear to me is the inner difference between -setNeedsLayout and -setNeedsUpdateConstraints. From Apple Docs:

setNeedsLayout

Call this method on your application’s main thread when you want to adjust the layout of a view’s subviews. This method makes a note of the request and returns immediately. Because this method does not force an immediate update, but instead waits for the next update cycle, you can use it to invalidate the layout of multiple views before any of those views are updated. This behavior allows you to consolidate all of your layout updates to one update cycle, which is usually better for performance.

setNeedsUpdateConstraints

When a property of your custom view changes in a way that would impact constraints, you can call this method to indicate that the constraints need to be updated at some point in the future. The system will then call updateConstraints as part of its normal layout pass. Updating constraints all at once just before they are needed ensures that you don’t needlessly recalculate constraints when multiple changes are made to your view in between layout passes.

When I want to animate a view after modifying a constraint and animate the changes I usually call for instance:

[UIView animateWithDuration:1.0f delay:0.0f usingSpringWithDamping:0.5f initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.modifConstrView setNeedsUpdateConstraints];
        [self.modifConstrView layoutIfNeeded];
    } completion:NULL];

I've found out that if I use -setNeedsLayout instead of -setNeedsUpdateConstraints everything work as expected, but if I change -layoutIfNeeded with -updateConstraintsIfNeeded, the animation won't happen.
I've tried to make my own conclusion:

-updateConstraintsIfNeeded only update constraints but doesn't force the layout to come into the process, thus original frames are still preserved

-setNeedsLayout calls also -updateContraints method

So when is ok to use one instead of the other? and about the layout methods, do I need to call them on the view that has a change in a constraint or on the parent view?

I don't understand people downvoting... really. S.O. you should do something about it, like asking a reason as mandatory or they are totally pointless
Perhaps they just need to obtain the Critic badge (First down vote)
I highly recommend you to see here. The answer is more of solution to a real problem. Also see this video

S
SmileBot

Your conclusions are right. The basic scheme is:

setNeedsUpdateConstraints makes sure a future call to updateConstraintsIfNeeded calls updateConstraints.

setNeedsLayout makes sure a future call to layoutIfNeeded calls layoutSubviews.

When layoutSubviews is called, it also calls updateConstraintsIfNeeded, so calling it manually is rarely needed in my experience. In fact, I have never called it except when debugging layouts.

Updating constraints using setNeedsUpdateConstraints is pretty rare too, objc.io–a must read about autolayouts–says:

If something changes later on that invalidates one of your constraints, you should remove the constraint immediately and call setNeedsUpdateConstraints. In fact, that’s the only case where you should have to trigger a constraint update pass.

In addition, in my experience, I have never had to invalidate constraints, and not set the setNeedsLayout in the next line of the code, because new constraints pretty much are asking for a new layout.

The rules of thumb are:

If you manipulated constraints directly, call setNeedsLayout.

If you changed some conditions (like offsets or smth) which would change constraints in your overridden updateConstraints method (a recommended way to change constraints, btw), call setNeedsUpdateConstraints, and most of the time, setNeedsLayout after that.

If you need any of the actions above to have immediate effect—e.g. when your need to learn new frame height after a layout pass—append it with a layoutIfNeeded.

Also, in your animation code, I believe setNeedsUpdateConstraints is unneeded, since constraints are updated before the animation manually, and the animation only re-lays-out the view based on differences between the old and new ones.


@coverback, so objc.io says "If something changes later on that invalidates one of your constraints, you should remove the constraint immediately and call setNeedsUpdateConstraints. In fact, that’s the only case where you should have to trigger a constraint update pass." And then in Animation block it says that when I remove, add or change constraint.contant I have to call setNeedsLayout. What's the difference? I feel real stupid :(
@pash3r Difference is updating constant doesn't qualify as "invalidation". Invalidation is when it's no longer relevant at all, like has to be attached to another view or removed completely. Constant would merely place a view closer or farther, or change its size, thus the need for setNeedsLayout.
@coverback setNeedsLayout makes sure layoutSubviews will be called in the next updating cycle, but perhaps this does nothing to do with layoutIfNeeded?
@coverback If you manipulate constraints directly, layoutSubviews will be called automatically, no need to call setNeedsLayout
Yes, manipulating a constraint's properties directly will trigger layoutSubviews, so no need to do it manually. You do, however, have to call layoutIfNeeded if you need the changes to take effect immediately instead of the next layout cycle
s
shim

The answer by coverback is pretty correct. However, I would like to add some additional details.

Below is the diagram of a typical UIView cycle which explains other behaviors:

https://i.stack.imgur.com/i9YuN.png

I've found out that if I use -setNeedsLayout instead of -setNeedsUpdateConstraints everything work as expected, but if I change -layoutIfNeeded with -updateConstraintsIfNeeded, the animation won't happen.

updateConstraints typically doesn't do anything. It just resolves constraints it doesn't apply them till layoutSubviews is called. So animation does requires a call to layoutSubviews.

setNeedsLayout calls also -updateContraints method

No this is not necessary. If your constraints haven't been modified UIView will skip call to updateConstraints. You need to explicitly call setNeedsUpdateConstraint to modify constraints in the process.

In order to call updateConstraints you need to do the following:

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];

Thanks, this solved my issue. I had a UIWindow with no parent UIView that had temporary constraints being added to it upon calling LayoutIfNeeded() before an animation. Adding a subview wrapper to the UIWindow and calling these three methods on it fixed my issue.
I don't think that calling layoutIfNeeded right after the setNeedsLayout is correct. Because the methods do the same despite the fact that one is causing layout to be redrawn immediately and the second one in a next update cycle.