setNeedsLayout与setNeedsUpdateConstraints和layoutIfNeeded与updateConstraintsIfNeeded


227

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

  1. 更新约束
  2. 布局视图(这是我们计算框架的位置)
  3. 显示

对我来说,还不完全清楚-setNeedsLayout和之间的内在区别-setNeedsUpdateConstraints。从Apple Docs:

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方法

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


27
我不明白人们会否投票...真的。因此,您应该对此做些事情,例如询问强制性理由,或者它们完全没有意义
Andrea

7
也许他们只需要获取评论家徽章(首次投票)
fujianjin6471 2015年

1
我强烈建议您在这里看看。答案更多是解决实际问题的方法。另请参阅此视频
Honey

Answers:


258

您的结论是正确的。基本方案是:

  • setNeedsUpdateConstraints确保将来有updateConstraintsIfNeeded来电updateConstraints
  • setNeedsLayout确保将来有layoutIfNeeded来电layoutSubviews

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

使用约束更新约束的情况setNeedsUpdateConstraints也很少见,objc.io –a必须阅读有关自动布局的信息–声明

如果以后发生的更改使您的约束之一无效,则应立即删除约束并调用setNeedsUpdateConstraints。实际上,这是唯一必须触发约束更新阶段的情况。

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

经验法则是:

  • 如果您直接操纵约束,请致电setNeedsLayout
  • 如果您更改了某些条件(例如偏移量或smth),这些条件更改您的重写updateConstraints方法中的约束(建议的约束更改方式,btw),请setNeedsUpdateConstraints在此setNeedsLayout之后的大部分时间调用。
  • 如果您需要上述任何操作立即生效(例如,当您需要在布局通过后了解新的框架高度时),请在后面附加一个layoutIfNeeded

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


@coverback,因此objc.io表示“如果以后发生的某些更改使您的约束之一无效,则应立即删除约束并调用setNeedsUpdateConstraints。实际上,这是唯一必须触发约束更新通过的情况。” 然后在Animation块中说,当我删除,添加或更改constraint.contant时,我必须调用setNeedsLayout。有什么不同?我感到很愚蠢:(
pash3r 2014年

3
@ pash3r差异在于更新常量不符合“无效”的条件。失效是当它不再相关时,例如必须附加到另一个视图或将其完全删除。常量只会使视图更近或更远,或者改变其大小,因此需要setNeedsLayout
掩护

@coverback setNeedsLayout确保layoutSubviews在下一个更新周期中将被调用,但是也许与这无关layoutIfNeeded
fujianjin6471 2015年

2
@coverback如果您直接操作约束,layoutSubviews将自动调用,无需调用setNeedsLayout
fujianjin6471 2015年

是的,直接操作约束的属性将触发layoutSubviews,因此无需手动执行。你这样做,不过,得叫layoutIfNeeded,如果你需要更改立即生效,而不是下一个布局周期
查理·马丁

89

Coverback答案非常正确。但是,我想补充一些其他细节。

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

UIView的生命周期

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

updateConstraints通常什么都不做。它只是解决约束,直到layoutSubviews被调用才应用约束。因此动画确实需要调用layoutSubviews

  1. setNeedsLayout也调用-updateContraints方法

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

要致电,updateConstraints您需要执行以下操作:

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

谢谢,这解决了我的问题。我有一个没有父UIView的UIWindow,该UIWindow在动画之前调用LayoutIfNeeded()时向其添加了临时约束。将子视图包装器添加到UIWindow并对其调用这三个方法解决了我的问题。
masterwok

我认为在setNeedsLayout之后立即调用layoutIfNeeded是正确的。因为尽管一种方法导致布局立即被重绘,而第二种在下一个更新周期中重新绘制,但这些方法的作用相同。
fillky
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.