ChatGPT解决这个技术问题 Extra ChatGPT

setNeedsLayout 与 setNeedsUpdateConstraints 和 layoutIfNeeded 与 updateConstraintsIfNeeded

我知道自动布局链基本上包含 3 个不同的过程。

更新约束布局视图(这里是我们计算帧的地方)显示

我不完全清楚的是 -setNeedsLayout-setNeedsUpdateConstraints 之间的内在区别。来自 Apple 文档:

setNeedsLayout

当您想要调整视图的子视图的布局时,请在应用程序的主线程上调用此方法。此方法记录请求并立即返回。由于此方法不强制立即更新,而是等待下一个更新周期,因此您可以使用它在更新这些视图中的任何一个之前使多个视图的布局无效。此行为允许您将所有布局更新合并到一个更新周期,这通常对性能更好。

setNeedsUpdateConstraints

当您的自定义视图的属性以影响约束的方式发生变化时,您可以调用此方法来指示约束需要在将来的某个时间点进行更新。然后系统将调用 updateConstraints 作为其正常布局传递的一部分。在需要之前一次性更新所有约束,确保在布局传递之间对视图进行多项更改时,您不会不必要地重新计算约束。

当我想在修改约束后为视图设置动画并为我通常调用的更改设置动画时,例如:

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

我发现如果我使用 -setNeedsLayout 而不是 -setNeedsUpdateConstraints,一切都会按预期工作,但如果我将 -layoutIfNeeded 更改为 -updateConstraintsIfNeeded,动画将不会发生。
我尝试制作我自己的结论:

-updateConstraintsIfNeeded 仅更新约束但不强制布局进入流程,因此仍保留原始帧

-setNeedsLayout 还调用 -updateContraints 方法

那么什么时候可以使用一个而不是另一个呢?关于布局方法,我是否需要在约束发生变化的视图或父视图上调用它们?

我不明白人们投票反对......真的。所以你应该做点什么,比如问一个强制性的理由,或者他们完全没有意义
也许他们只需要获得评论家徽章(第一次投反对票)
我强烈建议您查看 here。答案更多的是解决实际问题。另见this video

S
SmileBot

你的结论是对的。基本方案是:

setNeedsUpdateConstraints 确保将来调用 updateConstraintsIfNeeded 调用 updateConstraints。

setNeedsLayout 确保将来对 layoutIfNeeded 的调用会调用 layoutSubviews。

调用 layoutSubviews 时,它也会调用 updateConstraintsIfNeeded,因此根据我的经验,很少需要手动调用它。事实上,除了调试布局时,我从未调用过它。

使用 setNeedsUpdateConstraints 更新约束也非常少见,objc.io–a must read about autolayouts–says

如果稍后发生某些更改使您的约束之一无效,则应立即删除该约束并调用 setNeedsUpdateConstraints。事实上,这是您必须触发约束更新传递的唯一情况。

此外,根据我的经验,我从来不需要使约束无效,也不必在代码的下一行设置 setNeedsLayout,因为新约束几乎要求新布局。

经验法则是:

如果您直接操作约束,请调用 setNeedsLayout。

如果您更改了一些条件(如偏移量或 smth),这些条件会更改覆盖的 updateConstraints 方法中的约束(顺便说一句,这是更改约束的推荐方法),请调用 setNeedsUpdateConstraints,然后在大多数情况下调用 setNeedsLayout。

如果您需要上述任何操作来立即生效——例如,当您需要在布局通过后学习新的帧高度时——将其附加一个 layoutIfNeeded。

另外,在您的动画代码中,我认为不需要 setNeedsUpdateConstraints,因为在动画之前手动更新约束,并且动画仅根据新旧视图之间的差异重新布置视图。


@coverback,所以 objc.io 说“如果稍后发生某些更改使您的约束之一无效,您应该立即删除约束并调用 setNeedsUpdateConstraints。事实上,这是您应该触发约束更新传递的唯一情况。”然后在动画块中它说当我删除、添加或更改约束时,我必须调用 setNeedsLayout。有什么不同?我觉得真的很愚蠢:(
@pash3r 不同之处在于更新常量不符合“无效”的条件。失效是当它不再相关时,例如必须附加到另一个视图或完全删除。常量只会将视图放置得更近或更远,或者改变它的大小,因此需要 setNeedsLayout
@coverback setNeedsLayout 确保 layoutSubviews 将在下一个更新周期中被调用,但这可能与 layoutIfNeeded 无关?
@coverback 如果直接操作约束,会自动调用 layoutSubviews,不需要调用 setNeedsLayout
是的,直接操作约束的属性会触发 layoutSubviews,因此无需手动操作。但是,如果您需要更改立即生效而不是下一个布局周期,则必须调用 layoutIfNeeded
s
shim

answer by coverback 非常正确。但是,我想补充一些额外的细节。

下面是一个典型的 UIView 循环图,它解释了其他行为:

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

我发现如果我使用 -setNeedsLayout 而不是 -setNeedsUpdateConstraints 一切都会按预期工作,但如果我将 -layoutIfNeeded 更改为 -updateConstraintsIfNeeded,动画将不会发生。

updateConstraints 通常不执行任何操作。它只是解决在调用 layoutSubviews 之前不会应用它们的约束。所以动画确实需要调用 layoutSubviews

setNeedsLayout 还调用 -updateContraints 方法

不,这不是必需的。如果您的约束尚未修改,UIView 将跳过对 updateConstraints 的调用。您需要显式调用 setNeedsUpdateConstraint 来修改过程中的约束。

为了调用 updateConstraints,您需要执行以下操作:

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

谢谢,这解决了我的问题。我有一个没有父 UIView 的 UIWindow,它在动画之前调用 LayoutIfNeeded() 时添加了临时约束。将子视图包装器添加到 UIWindow 并在其上调用这三个方法解决了我的问题。
我认为在 setNeedsLayout 之后立即调用 layoutIfNeeded 是不正确的。因为这些方法的作用相同,尽管一个方法会导致布局立即重绘,而第二个方法会在下一个更新周期中重新绘制。