我什么时候可以激活/停用布局约束?

tyb*_*103 97 uiview ios autolayout nslayoutconstraint swift

我在IB中设置了多组约束,我想根据某些状态以编程方式在它们之间切换.有一个constraintsA出口集合,所有出口集合都标记为从IB安装,constraintsB出口集合全部在IB中卸载.

我可以编程方式在两个集之间切换,如下所示:

NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)
Run Code Online (Sandbox Code Playgroud)

但是......我无法弄清楚何时这样做.看起来我应该能够做到这一点viewDidLoad,但我不能让它工作.我已经尝试过调用view.updateConstraints()view.layoutSubviews()在设置约束后,但无济于事.

我确实发现,如果我设置约束,viewDidLayoutSubviews一切都按预期工作.我想我想知道两件事......

  1. 为什么我会出现这种行为?
  2. 是否可以从viewDidLoad激活/停用约束?

小智 168

我激活和停用NSLayoutConstraintsviewDidLoad,和我没有任何问题,它.所以它确实有效.您的应用和我的应用之间的设置必须有所不同:-)

我只是描述我的设置 - 也许它可以给你带头:

  1. 我设置了@IBOutlets激活/停用所需的所有约束.
  2. ViewController,我将约束保存到非弱的类属性中.原因是我发现在停用约束后,我无法重新激活它 - 它是零.因此,它似乎在停用时被删除.
  3. NSLayoutConstraint.deactivate/activate不像你那样使用,我使用constraint.active = YES/ NO代替.
  4. 设置约束后,我打电话view.layoutIfNeeded().

  • "将约束保存到不弱的类属性中"你节省了我很多时间,谢谢! (116认同)
  • "我将约束保存到不弱的类属性中":这为我节省了大量的心痛.我不知道我在nil对象上调用了一个选择器.谢谢!! (8认同)
  • 需要注意的是,自动布局不会忽略"非活动"约束,它们会被删除.激活/停用约束实际上会添加它们并将其删除.花了一些时间调试冲突的自动布局后,我添加了我之前设置的`.active = false`约束,期望在我将它们设置为活动状态之前它们将被忽略. (2认同)
  • Apples doc说:激活或取消激活约束会在视图上调用addConstraint(_ :)和removeConstraint(_ :),该视图是此约束管理的项目的最近共同祖先.使用此属性而不是直接调用addConstraint(_ :)或removeConstraint(_ :).因此,似乎当约束被停用时,它被移除,然后没有对约束的强引用,除非IBOutlet很强.因此约束被删除.恕我直言,这几乎是一个错误或至少非常意外的行为. (2认同)
  • 用户stefan在对该问题的评论中提交了解释,将约束标记为未安装在IB中专门用于仅与尺寸类一起使用,而不是用于设计在实例化视图时停用的约束。它几乎可以工作,但是有一个微妙的错误或不兼容性导致了OP所看到的结果。您必须保留 IB 中安装的所有约束,或者在代码中添加停用的约束。在我的应用程序中,viewDidLayoutSubviews 解决方案不起作用,不使用已卸载的约束却不起作用。请参阅/sf/answers/1936478701/ (2认同)

Leo*_*Leo 45

也许你可以检查一下@properties,替换weakstrong.

有时因为active = NO设置self.yourConstraint = nil,所以你不能再使用self.yourConstraint了.

  • 如[Swift语言指南](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID48)所述,默认情况下属性很强所以你也可以删除`weak`,这样就可以了. (4认同)

小智 29

override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}
Run Code Online (Sandbox Code Playgroud)


Ash*_*Ash 14

我相信您遇到的问题是由于在viewDidLoad()调用AFTER之前没有将约束添加到其视图中.你有很多选择:

A)您可以将布局约束连接到IBOutlet,并通过这些引用在代码中访问它们.由于插座在viewDidLoad()开始之前已连接,因此应该可以访问约束,您可以继续在那里激活和停用它们.

B)如果你想使用UIView的constraints()函数来访问各种约束,你必须等待viewDidLayoutSubviews()开始并在那里开始,因为这是从一个nib创建一个视图控制器之后的第一点,它将具有任何已安装的约束.完成后不要忘记打电话layoutIfNeeded().这样做的缺点是,如果要应用任何更改,布局传递将执行两次,并且您必须确保不会触发无限循环.

快速警告: 方法不会返回禁用的约束constraints() !这意味着如果您要禁用约束以便稍后再打开它,则需要保留对它的引用.

C)您可以忘记故事板方法并手动添加约束.由于你这样做,viewDidLoad()我认为目的只是在对象的整个生命周期内只进行一次,而不是动态地改变布局,所以这应该是一种可接受的方法.


use*_*149 9

您还可以调整priority属性以"启用"和"禁用"它们(例如,750值启用,250禁用).出于某种原因,更改activeBOOL对我的UI没有任何影响.不需要layoutIfNeeded也可以在viewDidLoad或之后的任何时间设置和更改.


Las*_*zlo 8

停用未使用的约束的适当时间:

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    self.myLittleConstraint.active = NO;
}
Run Code Online (Sandbox Code Playgroud)

请记住,viewWillLayoutSubviews可以多次调用,所以这里没有繁重的计算,好吗?

注意:如果您想稍后反应某些约束,则始终存储strong对它们的引用.

  • 对我来说,唯一可靠的方法是调整`viewDidLayoutSubviews()`中的约束.调整`viewWillLayoutSubviews()`中的约束在我的情况下不起作用. (2认同)

Sum*_*eet 5

创建视图时,将按顺序调用以下生命周期方法:

  1. 加载视图
  2. 查看已加载
  3. 视图将出现
  4. viewWillLayoutSubviews
  5. viewDidLayoutSubviews
  6. 视图DidAppear

现在回答你的问题。

  1. 为什么我会出现这种行为?

答:因为当你试图在viewDidLoad视图上设置约束时,视图没有其边界,因此无法设置约束。只有在viewDidLayoutSubviews最终确定视图的边界之后。

  1. 是否可以从 viewDidLoad 激活/停用约束?

答案:否。原因如上所述。