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

Posted

技术标签:

【中文标题】NSLayoutConstraint 的 Unsatisfiable Constraint 以编程方式更改 - swift【英文标题】:Unsatisfiable Constraint for NSLayoutConstraint changed programmatically - swift 【发布时间】:2020-10-04 10:02:05 【问题描述】:

我在 viewDidLoad 中设置了一个约束,然后在键盘出现时更改它的常量。 这是初始设置

bottomConstraint = NSLayoutConstraint(item: bottomBar, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
    view.addConstraint(bottomConstraint)

然后我在收到键盘通知时更改常量:

@objc func handleKeyboardNotification(notification: NSNotification)
    
    if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue 
        let keyboardRectangle = keyboardFrame.cgRectValue
        let keyboardHeight = keyboardRectangle.height
        
        let isKeyboardShowing = notification.name == UIResponder.keyboardWillShowNotification
        
        bottomConstraint?.constant = isKeyboardShowing ? -keyboardHeight : 0
        
        UIView.animate(withDuration:0.1, delay: 0 , options: .curveEaseOut , animations: 
            self.view.layoutIfNeeded()
         , completion: (completed) in
        )
    

这很有趣,因为我只是更改常量而不添加另一个约束。尽管如此,我还是在控制台中收到了这个警告:

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:0x282ae4ff0 UIView:0x109f11110.bottom == UIView:0x109f13240.bottom   (active)>",
"<NSLayoutConstraint:0x282ae8050 UIView:0x109f11110.bottom == UIView:0x109f13240.bottom - 291   (active)>"
 )

这基本上表明我的限制不能很好地协同工作。 我不知道我做错了什么。

编辑: 这是我用来以编程方式添加底栏的代码:

let bottomBar:UIView = 
    let v = UIView()
    v.translatesAutoresizingMaskIntoConstraints = false
    return v
()

在 ViewdidLoad() 中

view.addSubview(bottomBar)
bottomBar.addSubview(fontView)
bottomBar.addSubview(colorPicker)
fontView.pin(to: bottomBar)
colorPicker.pin(to: bottomBar)

 func setUpConstraints()
 NSLayoutConstraint.activate([
        bottomBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        bottomBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        bottomBar.heightAnchor.constraint(equalToConstant: 70),
 ])

【问题讨论】:

bottomBar.translatesAutoresizingMaskIntoConstraints 是真是假? @Sweeper 是假的 您是否使用情节提要作为视图? 不,都是程序化创建的 @Stackgu 我认为@DonMag 指的是bottomViewConstraintbottomConstraint 是两个不同的变量。 【参考方案1】:

试图让你更轻松一些......

从简单开始。

这段代码在顶部附近创建了一个文本字段,并在底部创建了一个红色的“bottomBar”视图。当键盘显示或隐藏时,bottomBar 的底部约束常量会更新(点击视图上的任意位置以关闭键盘):

class ConstraintTestViewController: UIViewController 
    
    let bottomBar = UIView()
    
    var bottomConstraint: NSLayoutConstraint!
    
    override func viewDidLoad() 
        super.viewDidLoad()
    
        // add a text field
        let tf = UITextField()
        tf.borderStyle = .roundedRect
        tf.translatesAutoresizingMaskIntoConstraints = false
        
        bottomBar.translatesAutoresizingMaskIntoConstraints = false
        bottomBar.backgroundColor = .red
        
        view.addSubview(tf)
        view.addSubview(bottomBar)

        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            
            // constrain text field 80-pts from Top, Width: 240, centerX
            tf.topAnchor.constraint(equalTo: g.topAnchor, constant: 80.0),
            tf.widthAnchor.constraint(equalToConstant: 240.0),
            tf.centerXAnchor.constraint(equalTo: g.centerXAnchor),
            
            // constrain bottomBar Leading and Trailing, Height: 70-pts
            bottomBar.leadingAnchor.constraint(equalTo: g.leadingAnchor),
            bottomBar.trailingAnchor.constraint(equalTo: g.trailingAnchor),
            bottomBar.heightAnchor.constraint(equalToConstant: 70.0),
            
        ])
        
        // create and add the bottom constraint
        bottomConstraint = NSLayoutConstraint(item: bottomBar, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
        view.addConstraint(bottomConstraint)

        // or, use more modern syntax...
        //bottomConstraint = bottomBar.bottomAnchor.constraint(equalTo: g.bottomAnchor)
        //bottomConstraint.isActive = true
        
        // keyboard show/hide notifications
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.handleKeyboardNotification(notification:)),
                                               name: UIResponder.keyboardWillShowNotification,
                                               object: nil)
        
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.handleKeyboardNotification(notification:)),
                                               name: UIResponder.keyboardWillHideNotification,
                                               object: nil)

        // add a "tap on the view to dismiss the keyboard" gesture
        let t = UITapGestureRecognizer(target: self, action: #selector(self.didTap(_:)))
        view.addGestureRecognizer(t)
        
    
    
    @objc func handleKeyboardNotification(notification: NSNotification)
        
        if let keyboardFrame: NSValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue 
            let keyboardRectangle = keyboardFrame.cgRectValue
            let keyboardHeight = keyboardRectangle.height
            
            let isKeyboardShowing = notification.name == UIResponder.keyboardWillShowNotification
            
            bottomConstraint.constant = isKeyboardShowing ? -keyboardHeight : 0
            
            UIView.animate(withDuration:0.1, delay: 0 , options: .curveEaseOut , animations: 
                self.view.layoutIfNeeded()
             , completion: (completed) in
            )
        
    
    
    @objc func didTap(_ g: UITapGestureRecognizer) -> Void 
        view.endEditing(true)
    
    

如果运行时没有自动布局错误/警告(它会),然后开始添加您的其他 UI 元素(和支持代码)一次一个 .如果/当你再次遇到约束冲突时,你就会确切地知道你哪里出错了。

【讨论】:

谢谢,我将从头开始,看看约束会发生什么以及底部约束导致错误的位置 我终于发现了它是什么,真是愚蠢的错误,我添加了两个用于初始化视图控制器的 sn-ps 代码:一个在 viewDidLoad 中(就像我习惯做的那样),另一个在自定义 init(nibName..),这导致创建双重约束。总之谢谢大家的支持!【参考方案2】:

你没有提供足够的信息,所以有必要猜测。这是一个猜测。当你说:

fontView.pin(to: bottomBar)
colorPicker.pin(to: bottomBar)

您创建 两个 bottomBar 底部约束。当你说:

bottomConstraint = NSLayoutConstraint(item: bottomBar, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
view.addConstraint(bottomConstraint)

你创建了一个第三个​​底部约束。

那么当你说

bottomConstraint?.constant = isKeyboardShowing ? -keyboardHeight : 0

您更改了一个底部约束,但没有更改其他约束。也许这就是冲突的原因。

另一种可能性是,您可能以某种未向我们展示的方式两次调用view.addConstraint。同样,这意味着您正在更改一个底部约束,而不是另一个。

【讨论】:

感谢您的回答,我考虑了您的两个选项,但似乎它们都不适用于我的情况,我注意到每当我更改 bottomConstraint 的常量时都会出现警告(如果我在不更改常量的情况下设置它,它可以正常工作)所以我在想可能是因为无论何时显示或不显示通知都会被调用两次 通知可以多次调用。但这不是问题。这是两个不同的约束。您可以在错误消息中清楚地看到这一点。你需要相信它告诉你的东西。这不是系统错误。这是你的错误。相信我,除非你相信这一点,否则你不会明白这一点。有很多方法可以计算出你的约束是什么;使用它们。使用视图调试器。给出您的约束标识符(名称)。做任何事情来调试。 我终于发现了它是什么,真是愚蠢的错误,我添加了两个用于初始化视图控制器的 sn-ps 代码:一个在 viewDidLoad 中(就像我习惯做的那样),另一个在自定义 init(nibName..),这导致创建双重约束。总之谢谢大家的支持! 干得好。所以我的最后一段是对的!你在这里看到了课程的价值。相信运行时间!它说有两个约束,有两个约束,就这么简单。

以上是关于NSLayoutConstraint 的 Unsatisfiable Constraint 以编程方式更改 - swift的主要内容,如果未能解决你的问题,请参考以下文章

NSLayoutConstraint 激活/停用

错误的 NSLayoutConstraint 导致黑屏

NSLayoutConstraint 设置优先级

如何检查 NSLayoutConstraint 是不是动画

无法获取 NSLayoutConstraint 的约束

更改 NSLayoutConstraint 的常量