79

我看到了设置约束的不同示例。有些将它们设置在viewDidLoad/中loadView(在添加子视图之后)。其他人将它们设置在方法updateViewConstraints中,由viewDidAppear.

当我尝试在其中设置约束时updateViewContraints,布局可能会出现跳跃,例如视图出现之前的轻微延迟。另外,如果我使用这种方法,我应该先清除现有的约束[self.view [removeConstraints:self.view.constraints]吗?

4

9 回答 9

108

viewDidLoad我在/中设置了约束loadView(我的目标是 iOS >= 6)。updateViewConstraints对于更改约束值很有用,例如,如果某些约束取决于屏幕的方向(我知道,这是一种不好的做法),您可以constant在此方法中更改它。

从 39:22 开始,在“iOS 和 OS X 的自动布局简介”viewDidLoad (WWDC 2012)会话中显示了添加约束。我认为这是在讲座中说的但没有出现在文档中的事情之一。

更新:我注意到在 View Controllers 的资源管理中设置约束的提及:

如果您更喜欢以编程方式创建视图,而不是使用情节提要,则可以通过覆盖视图控制器的loadView 方法来实现。您对此方法的实现应执行以下操作:

(...)

3.如果您使用自动布局,请为您刚刚创建的每个视图分配足够的约束来控制视图的位置和大小。否则,实现viewWillLayoutSubviewsviewDidLayoutSubviews方法来调整视图层次结构中子视图的框架。请参阅“调整视图控制器的视图大小”。</p>

更新 2:在 WWDC 2015 期间,Apple给出了新的解释和推荐用法:updateConstraintsupdateViewConstraints

确实,所有这些都是视图有机会在下一次布局传递时及时更改约束的一种方式,但实际上通常不需要。

理想情况下,所有初始约束设置都应该在 Interface Builder 中进行。

或者如果你真的发现你需要以编程方式分配你的约束,像 viewDidLoad 这样的地方会好得多。

更新约束实际上只适用于需要定期重复的工作。

此外,当您发现需要这样做时,只需更改约束也非常简单;然而,如果您将该逻辑与与其相关的其他代码分开并将其移动到稍后执行的单独方法中,您的代码将变得更难遵循,因此您将更难维护,其他人会更难理解。

那么什么时候需要使用更新约束呢?

好吧,归结为性能。

如果您发现仅在适当位置更改约束太慢,那么更新约束可能会帮助您。

事实证明,在更新约束中更改约束实际上比在其他时间更改约束要快。

这样做的原因是因为引擎能够将在此通道中发生的所有约束更改视为批处理。

于 2013-10-15T19:12:52.050 回答
33

我建议创建一个 BOOL 并将它们设置在-updateConstraintsUIView 中(或者-updateViewConstraints,对于 UIViewController)。

-[UIView updateConstraints]: (苹果文档)

自己设置约束的自定义视图应该通过覆盖此方法来实现。

两者-updateConstraints-updateViewConstraints可能在视图的生命周期内被多次调用。(例如,调用setNeedsUpdateConstraints视图会触发这种情况。)因此,您需要确保防止创建和激活重复的约束——要么使用 BOOL 只执行一次特定的约束设置,要么确保在创建和激活新约束之前停用/删除现有约束。

例如:

  - (void)updateConstraints {  // for view controllers, use -updateViewConstraints

         if (!_hasLoadedConstraints) {
              _hasLoadedConstraints = YES;
             // create your constraints
         }
         [super updateConstraints];
    }

在评论中为@fresidue 欢呼,因为它指出 Apple 的文档建议将调用super作为最后一步。如果您super在更改某些约束之前调用,您可能会遇到运行时异常(崩溃)。

于 2013-10-16T08:31:37.623 回答
4

根据 Apple 的 WWDC 视频和文档,这应该在 ViewDidLoad 中完成。

不知道为什么人们推荐 updateConstraints。如果您在 updateConstraints 中执行此操作,您将遇到自动调整大小的 NSAutoresizingMaskLayoutConstraint 问题,因为您的视图已经考虑了自动掩码。您需要在 updateConstraints 中删除它们才能正常工作。

当您需要“更新”它们、从初始设置中进行更改等时,UpdateConstraints 应该就是为了这一点。

于 2018-03-17T15:03:32.137 回答
2

Do it in view did layout subviews 方法

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}
于 2016-07-25T07:48:40.470 回答
1

我有这个解决方案可以在加载情节提要中的内容之前更改约束。此解决方案消除了加载视图后的任何滞后。

-(void)updateViewConstraints{

    dispatch_async(dispatch_get_main_queue(), ^{

            //Modify here your Constraint -> Activate the new constraint and deactivate the old one

            self.yourContraintA.active = true;
            self.yourContraintB.active= false;
            //ecc..
           });

    [super updateViewConstraints]; // This must be the last thing that you do here -> if! ->Crash!
}
于 2016-03-10T16:40:38.613 回答
1

您也可以在viewWillLayoutSubviews:中设置它们:

 override func viewWillLayoutSubviews() {

    if(!wasViewLoaded){
        wasViewLoaded = true

        //update constraint

        //also maybe add a subview            
    }
}
于 2016-07-20T05:14:09.897 回答
0

添加约束viewWillLayoutSubviews()以编程方式添加约束

请参阅自定义布局部分中的Apple 文档

如果可能,请使用约束来定义所有布局。生成的布局更健壮且更易于调试。当您需要创建一个无法单独使用约束表达的布局时,您应该只覆盖 viewWillLayoutSubviews 或 layoutSubviews 方法。

于 2021-04-29T21:20:35.777 回答
0

以下示例是将任何视图传递给另一个类。从情节提要创建我的视图

斯威夫特 5.0

    override func viewWillAppear(_ animated: Bool) {
        
      super.viewWillAppear(animated) 
        DispatchQueue.main.async {
            self.abcInstance = ABC(frame: self.myView.frame)
          } 
      }

 

如果你错过 DispatchQueue.main.async了,更新约束需要时间viewWillAppear。在 storyboard 中创建 myView 并给出与屏幕宽度和高度相同的约束,然后尝试打印 myView 的框架。它将给出准确的值 inDispatchQueue.main.async或 inviewDidAppear但不给出准确的值viewWillAppearwithout DispatchQueue.main.async

于 2020-08-04T09:40:15.473 回答
0

这对我有用:

斯威夫特 4.2

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

// Modify your constraints in here

  ...

}

虽然老实说我不确定这是否值得。加载似乎比在 viewDidLoad() 中要慢一些。我只是想将它们从后者中移出,因为它变得越来越庞大。

于 2018-08-14T17:41:25.813 回答