安全地更改 NSLayoutConstraint 常量值

Posted

技术标签:

【中文标题】安全地更改 NSLayoutConstraint 常量值【英文标题】:Safely change NSLayoutConstraint constant value 【发布时间】:2019-06-19 08:40:37 【问题描述】:

我的UIView 对象有底部约束,我想更改它的值。完成后,视图确实显示正确,但是,我在控制台中看到错误日志;

[LayoutConstraints] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x600003cbe670 PSBApp.KeyboardView:0x7fc5f760e010.bottom == UIView:0x7fc5f4503090.bottom + 306   (active)>",
    "<NSLayoutConstraint:0x600003c8cbe0 PSBApp.KeyboardView:0x7fc5f760e010.bottom == UIView:0x7fc5f4503090.bottom - 10   (active)>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600003cbe670 PSBApp.KeyboardView:0x7fc5f760e010.bottom == UIView:0x7fc5f4503090.bottom + 306   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

我的代码是:

private func setupKeyboard()
    keyboard.delegate = self
    view.addSubview(keyboard)
    keyboardBottomConstraint = keyboard.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: keyboard.expectedHeight())
    keyboard.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    keyboardBottomConstraint.isActive = true
  

  private func showKeyboard()
    let bottomOffset: CGFloat = 10
    keyboardBottomConstraint.constant = -bottomOffset
  

所以,在我setupKeyboard 之后没有错误并且屏幕上没有可见视图,然后,在我调用showKeyboard 之后它变得可见,但出现了错误。

【问题讨论】:

看起来您有 两个 底部约束。你有没有可能给setupKeyboard()打过两次电话? @DonMag 是的,我确实做到了,我确实错误地调用了 setupKeyboard() 两次,感谢您的通知,现在问题解决了! 我将添加(附加解释)作为答案,以帮助遇到此问题的其他人。 @DonMag 是的,虽然它并不完全依赖于 AutoLayout 机制,但我仍然认为错误是可能的,尤其是当您在代码中设置约束时。 【参考方案1】:

有错误/警告信息是关键:

(
    "<NSLayoutConstraint:0x600003cbe670 PSBApp.KeyboardView:0x7fc5f760e010.bottom == UIView:0x7fc5f4503090.bottom + 306   (active)>",
    "<NSLayoutConstraint:0x600003c8cbe0 PSBApp.KeyboardView:0x7fc5f760e010.bottom == UIView:0x7fc5f4503090.bottom - 10   (active)>"
)

如您所见,KeyboardView两个 不能同时有效的底部约束。

根据 OP 的 cmets,setupKeyboard() 无意中被调用了两次,导致 2 个底部约束。

【讨论】:

【参考方案2】:

将底部约束优先级值设置为高(750)

【讨论】:

【参考方案3】:

您可以自动使用 layoutIfNeeded 和 setNeedsLayout:

layoutIfNeeded 强制接收器在需要时立即布局其子视图。

假设你已经重写了 layoutSubviews,并且 UIKit 认为你的视图出于任何原因需要布局(例如,你在处理某些用户操作时调用了 setNeedsLayout)。然后,您的自定义 layoutSubviews 方法将立即被调用,而不是在常规 UIKit 运行循环事件序列中通常调用它时(在事件处理之后,但在 drawRect 之前:)。

您可能需要在单个运行循环中调用 layoutIfNeeded 的示例:

您调整包含具有自定义布局的表格视图的自定义视图的大小。设置 setNeedsLayout 以便稍后调用 layoutSubviews。 控制器对象在处理用户事件时要求表格视图滚动到某个特定的单元格。 您的自定义视图在 layoutSubviews 中执行一些自定义大小的表格视图,以更改表格视图的大小。 问题是当控制器要求表格视图滚动(第 2 步)时,表格视图的边界已经过时。更新的边界稍后将仅在表视图上设置(第 3 步)。在 layoutSubviews 完成后,控制器希望表格视图滚动到的内容实际上可能不可见。一个解决方案是控制器在它知道这可能发生的情况下调用 layoutIfNeeded。

【讨论】:

我确实尝试添加 view.setNeedsLayout() view.layoutIfNeeded() 但错误仍然存​​在。

以上是关于安全地更改 NSLayoutConstraint 常量值的主要内容,如果未能解决你的问题,请参考以下文章

重新激活 NSLayoutConstraint 在展开可选时发现 nil

更改 NSLayoutConstraint 时的 SWIFT 动画

NSLayoutConstraint 更改后,systemLayoutSizeFittingSize 不起作用

如何以编程方式更改或更新 NSLayoutConstraint

我可以更改 NSLayoutConstraint 的乘数属性吗?

NSLayoutConstraint 的 Unsatisfiable Constraint 以编程方式更改 - swift