自动布局和编程约束:如何处理多次触发的 updateConstraints?
Posted
技术标签:
【中文标题】自动布局和编程约束:如何处理多次触发的 updateConstraints?【英文标题】:Autolayout & programmatic constraints: How to deal with updateConstraints firing multiple times? 【发布时间】:2014-07-07 14:46:59 【问题描述】:当以编程方式创建布局时,我遵循 Apple 的建议:覆盖 -updateConstraints,添加自定义约束,并在将子视图添加到视图后调用 -setNeedsUpdateConstraints。我的典型设置如下所示:
- (void)setupViews
//Style View
//Add gesture recognizers
//Add Subviews
[self setNeedsUpdateConstraints];
- (void)updateConstraints
//Add custom constraints
[super updateConstraints];
问题
在某些情况下 -updateConstraints 会被多次触发(例如,当视图的控制器被呈现或推送时带有动画)。这里的问题是添加的每个约束都会重新添加。当尝试按需更改添加约束的常数时,这会成为一个严重的问题,因为有两个原始约束随后会相互冲突。我想,即使您在创建约束后没有对其进行操作,也会让您看起来不太好。
可能的解决方案
1 - 在 -updateConstraints 中应用之前移除所有影响视图的约束:
- (void)updateConstraints
//Remove all constraints affecting view & subviews
//Add custom constraints
[super updateConstraints];
2 - 设置布局标志并在添加自定义约束之前对其进行检查:
- (void)updateConstraints
if (self.didAddConstraints)
[super updateConstraints];
return;
//Add custom constraints
self.didAddConstraints = YES;
[super updateConstraints];
3 - 不必担心加倍约束,并且每当需要更改常量时,只需在重新添加之前删除该约束即可。
3 - 我没想到的很棒的东西。
这里的最佳做法是什么?
【问题讨论】:
我认为您可能误用了setNeedsUpdateConstraints
和updateConstraints
。你提到了更新常量——在这种情况下,你只需要setNeedsLayout
。我的理解是updateConstraints
旨在对布局进行大规模更改-如果您要更改视图的结构,而不仅仅是参数。它旨在有一个地方/时间来显着改变约束,以避免在小的状态更改后更新的搅动。也就是说,在状态属性设置代码中,你也更新了具体对应的约束,调用setNeedsDisplay。
【参考方案1】:
简答:可能的解决方案编号 2。
随着布局变得越来越复杂,删除和重新应用所有约束可能会变得昂贵。此外,如果你的布局是有状态的,你会遇到更多的问题。
双重约束是非常低效的,你永远不知道updateConstraints
可能被调用了多少次。
正如this blog post 所示,使用标志是处理此问题的最简单、最有效的方法。我自己就是这样处理的。
作为旁注,您提到存在一种您尚未想到的很棒的方式。大多数时候,最简单的方法是最棒的方法。 :)
【讨论】:
我同意,这通常是 Apple UIKit 工程师推荐的。最重要的一点是避免在自动布局引擎完成布局传递后移除约束——移除一堆约束、重新添加更多约束、然后重新计算布局的成本令人难以置信。如果您确实想更改约束,请在现有约束上调整constant
属性,因为自动布局引擎重新解决它非常有效。
另外,请注意(正如 Apple 对这些方法的文档所述)您必须在实现的最后调用 super
。如果您在更改某些约束之前调用它,您可能会遇到运行时异常(崩溃)。通常这不是问题,但它可能发生在特定的约束条件下(例如,正在调用 -updateViewConstraints
方法的视图控制器的视图上的高度约束)。
我想知道在init
(包括awakeFromNib
)方法中添加约束有什么问题?我对额外的标志变量感到非常不舒服。
@CopperCash 不幸的是,布局引擎在从 nib 加载视图时还没有准备好。视图也需要成为视图层次结构的一部分才能应用约束。亲自尝试一下,并从遇到的错误中吸取教训。
@duci9y 我已经在我的UIView
s 的init
(包括awakeFromNib
)方法中使用了数百次AutoLayout。如果有什么不对劲,我相信我早就知道了。我只想知道在updateViewConstraints
而不是init
中设置约束是否有一些好处。【参考方案2】:
对于初始设置,也可以进行这种跟踪。在大多数情况下。
override func updateConstraints()
if constraints.count == 0
let views = ["textField": textField]
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-0-[textField]-0-|", options: [], metrics: nil, views: views))
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-0-[textField]-0-|", options: [], metrics: nil, views: views))
super.updateConstraints()
【讨论】:
以上是关于自动布局和编程约束:如何处理多次触发的 updateConstraints?的主要内容,如果未能解决你的问题,请参考以下文章