以编程方式创建视图时应在哪里设置自动布局约束


79

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

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


1
我在updateViewConstraints上也有过同样的经历,所以我停止尝试使用它。我在viewDidLoad或自定义视图的updateConstraints方法中配置约束。希望有人能给您确切的答案。
bilobatum

1
updateViewConstraints您可以在子类中重写此方法,以便向视图或其子视图添加约束。(来自Apple文档
测试

Answers:


108

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

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

更新:我注意到在View Controllers中提到了在Resource Management中设置约束的内容:

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

(...)

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

更新2:2015年WWDC期间苹果给予了新的解释updateConstraintsupdateViewConstraints推荐的用法:

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

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

或者,如果您真的发现需要以编程方式分配约束,则诸如viewDidLoad之类的位置会更好。

更新约束实际上只是针对需要定期重复的工作。

同样,当您发现需要更改约束时,这很简单。相反,如果您将该逻辑与其他与之相关的代码分开,然后将其移入一个单独的方法中,然后在以后执行,则代码将变得难以遵循,因此您将难以维护,其他人很难理解。

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

好吧,归结为性能。

如果您发现仅更改约束条件太慢,那么更新约束条件可能会为您提供帮助。

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

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


2
+1是最佳答案。Apple规定loadView是设置初始约束的正确位置,并且在updateConstraints方法中这不需要额外的BOOL标志(这似乎很hack)。
awolf

4
我认为视图应该负责约束,而不是视图控制器。在很多情况下,视图控制器甚至都不知道视图中的所有元素是什么(例如,表视图单元中的静态标签)。
dasdom

1
@dasdom视图如何控制其与其他视图的关系?您必须像@"|-[button1]-[button2]-|"在ViewController中那样设置约束,对吗?还是有其他方法?
约瑟夫

@Casper大多数时候,我都有一个UIView子类,它是视图控制器的视图。在该视图中,存在子视图和该子视图的约束。
dasdom 2014年

3
为什么更改in中的约束是不好的做法updateViewConstraints
测试时间

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喝彩,指出苹果的文档建议super在最后一步进行调用。如果super在更改某些约束之前调用,则可能会遇到运行时异常(崩溃)。


7
我不确定它在实用上是否有任何区别,但是文档说“重要:调用[super updateConstraints]作为实现的最后一步。”
fresidue

根据文档,如果您在运行时更改视图并使约束无效,则系统会立即删除该约束并调用setNeedsUpdateConstraints。在执行新布局之前,系统会调用updateConstraints,您可以在其中自定义无效的布局。因此,我可能不会在此方法上设置标志,这可能会阻止系统调用它。
smileBot 2015年

1
@cocoanutmobile如果您按照此答案的建议使用BOOL标志,则只是为了防止多次添加某些约束。这是一件非常好的事情。另一种选择是简单地存储对您创建的任何约束的引用,然后确保在创建和激活任何新约束之前先删除(取消激活)所有约束。但是,这种方法会产生较差的性能,尤其是当您的旧约束和新约束完全相同时。
smileyborg

2
@cocoanutmobile另外,请注意,此处的BOOL标志不会阻止系统调用-updateConstraints或任何操作,它仍会被调用,而您仍会调用[super updateConstraints]
smileyborg

1
作为@fresidue提到的,请确保调用super你实现的最末端-updateConstraints-updateViewConstraints。有关更多信息,请参见此评论
smileyborg

4

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

不知道为什么人们推荐updateConstraints。如果在updateConstraints中执行此操作,则会因自动调整大小而导致NSAutoresizingMaskLayoutConstraint出现问题,因为您的视图已经考虑了自动蒙版。您需要在updateConstraints中将其删除才能工作。

UpdateConstraints应该仅用于此目的,当您需要“更新”它们,从初始设置进行更改等时。


1
您可以添加您在此处引用的视频和文档的链接吗?
Shivam Pokhriyal

2

在视图中做布局子视图方法吗

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}

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!
}

1

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

 override func viewWillLayoutSubviews() {

    if(!wasViewLoaded){
        wasViewLoaded = true

        //update constraint

        //also maybe add a subview            
    }
}

0

这对我有用:

斯威夫特4.2

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

// Modify your constraints in here

  ...

}

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


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中的约束。在情节提要中创建myView并赋予与屏幕宽度和高度相同的约束,然后尝试打印myView的框架。它将在DispatchQueue.main.async或viewDidAppear中给出准确的值,但在没有DispatchQueue.main.async的viewWillAppear中给出准确的值。

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.