【问题标题】:setNeedsLayout vs. setNeedsUpdateConstraints and layoutIfNeeded vs updateConstraintsIfNeededsetNeedsLayout 与 setNeedsUpdateConstraints 和 layoutIfNeeded 与 updateConstraintsIfNeeded
【发布时间】:2014-01-03 17:49:18
【问题描述】:

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

  1. 更新约束
  2. 布局视图(这里是我们计算帧的地方)
  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

标签: ios objective-c autolayout nslayoutconstraint


【解决方案1】:

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

  • 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
【解决方案2】:

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

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

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

updateConstraints 通常什么都不做。它只是解决了在调用layoutSubviews 之前不会应用它们的约束。所以动画确实需要调用layoutSubviews

  1. setNeedsLayout 还调用 -updateContraints 方法

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

要拨打updateConstraints,您需要执行以下操作:

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

【讨论】:

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