自动布局和编程约束:如何处理多次触发的 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 - 我没想到的很棒的东西。


这里的最佳做法是什么?

【问题讨论】:

我认为您可能误用了setNeedsUpdateConstraintsupdateConstraints。你提到了更新常量——在这种情况下,你只需要setNeedsLayout。我的理解是updateConstraints 旨在对布局进行大规模更改-如果您要更改视图的结构,而不仅仅是参数。它旨在有一个地方/时间来显着改变约束,以避免在小的状态更改后更新的搅动。也就是说,在状态属性设置代码中,你也更新了具体对应的约束,调用setNeedsDisplay。 【参考方案1】:

简答:可能的解决方案编号 2。

随着布局变得越来越复杂,删除和重新应用所有约束可能会变得昂贵。此外,如果你的布局是有状态的,你会遇到更多的问题。

双重约束是非常低效的,你永远不知道updateConstraints可能被调用了多少次。

正如this blog post 所示,使用标志是处理此问题的最简单、最有效的方法。我自己就是这样处理的。

作为旁注,您提到存在一种您尚未想到的很棒的方式。大多数时候,最简单的方法是最棒的方法。 :)

【讨论】:

我同意,这通常是 Apple UIKit 工程师推荐的。最重要的一点是避免在自动布局引擎完成布局传递后移除约束——移除一堆约束、重新添加更多约束、然后重新计算布局的成本令人难以置信。如果您确实想更改约束,请在现有约束上调整 constant 属性,因为自动布局引擎重新解决它非常有效。 另外,请注意(正如 Apple 对这些方法的文档所述)您必须在实现的最后调用 super。如果您在更改某些约束之前调用它,您可能会遇到运行时异常(崩溃)。通常这不是问题,但它可能发生在特定的约束条件下(例如,正在调用 -updateViewConstraints 方法的视图控制器的视图上的高度约束)。 我想知道在init(包括awakeFromNib)方法中添加约束有什么问题?我对额外的标志变量感到非常不舒服。 @CopperCash 不幸的是,布局引擎在从 nib 加载视图时还没有准备好。视图也需要成为视图层次结构的一部分才能应用约束。亲自尝试一下,并从遇到的错误中吸取教训。 @duci9y 我已经在我的UIViews 的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?的主要内容,如果未能解决你的问题,请参考以下文章

读 SnapKit 和 Masonry 自动布局框架源码(轉)

决策树分类器如何处理全局约束?

没有图像的自动布局 UIImageView

GRPC 如何处理出现多次的指针?

CUDA 如何处理内存地址的多次更新?

如何处理具有排除约束的表的更新?