我什么时候可以激活/取消布局约束?

我已经在 IB 中设置了多组约束,我希望根据某些状态以编程方式在它们之间切换。有一个 constraintsA插座收集所有这些被标记为安装从 IB,和一个 constraintsB插座收集所有这些都是卸载在 IB。

我可以通过编程在两个集合之间切换,如下所示:

NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)

但是... 我不知道 when是怎么做到的。看起来我应该能在 viewDidLoad中做到一次,但是我不能让它工作。我尝试在设置约束之后调用 view.updateConstraints()view.layoutSubviews(),但是没有用。

我确实发现,如果我在 viewDidLayoutSubviews中设置约束,一切都会按预期运行。我想知道两件事。

  1. 为什么我会有这种行为?
  2. 是否可以从 viewDidLoad 激活/取消约束?
98917 次浏览
override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}

我激活和停用 viewDidLoad中的 NSLayoutConstraints,我没有任何问题。所以确实有用。你的应用程序和我的应用程序在设置上肯定有所不同: -)

我来描述一下我的设置——也许它能给你提供一条线索:

  1. 我为所有需要激活/停用的约束设置了 @IBOutlets
  2. In the ViewController, I save the constraints into class properties that are not weak. The reason for this is that I found that after deactivating a constraint, I could not reactivate it - it was nil. So, it seems to be deleted when deactivated.
  3. 我不像你一样使用 NSLayoutConstraint.deactivate/activate,我使用 constraint.active = YES/NO
  4. 在设置约束之后,我调用 view.layoutIfNeeded()

我相信你正在经历的问题是由于约束没有被添加到他们的意见,直到 viewDidLoad()被调用。你有很多选择:

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

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

一个简短的警告: constraints() < strong > < em > 方法不返回禁用的约束!这意味着,如果您确实禁用了一个约束,并打算稍后再次打开它,那么您将需要保留对它的引用。

C) 您可以忘记故事板方法,而手动添加约束。因为您是在 viewDidLoad()中执行这个操作的,所以我假设这样做的目的是在对象的整个生命周期中只执行一次,而不是动态更改布局,所以这应该是一种可以接受的方法。

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

  1. LoadView
  2. ViewDidLoad
  3. 出现
  4. 视图
  5. 视图
  6. 出现

Now to your questions.

  1. 为什么我会有这种行为?

答: 因为当您尝试在 viewDidLoad中设置视图的约束时,视图没有它的边界,因此不能设置约束。只有在 viewDidLayoutSubviews之后,视图的边界才最终确定。

  1. 是否可以从 viewDidLoad 激活/取消约束?

Answer: No. Reason explained above.

我发现,只要在 - (void)updateConstraints(目标 c)的覆盖中设置每个普通值的约束,使用 strong引用来表示使用的活动和非活动约束的初始值。在视图周期的其他地方停用和/或激活您需要的,然后调用 layoutIfNeeded,您应该没有问题。

主要的事情是不要不断地重用 updateConstraints的覆盖和分离约束的激活,只要在第一次初始化和布局之后调用 updateConstraint。之后在视图循环中的哪个位置似乎确实很重要。

取消未使用约束的适当时间:

-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];


self.myLittleConstraint.active = NO;
}

请记住 viewWillLayoutSubviews可以被多次调用,所以这里不需要进行大量的计算,好吗?

注意: 如果您想在以后对某些约束进行反应,那么总是存储对它们的 strong引用。

您还可以调整 priority属性以“启用”和“禁用”它们(例如,启用值为750,禁用值为250)。由于某种原因,更改 active BOOL 对我的 UI 没有任何影响。不需要 layoutIfNeeded,可以在 viewDidLoad 或之后的任何时间进行设置和更改。

也许你可以 检查你的 ABC0,用 ABC2代替 weak

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