Ethan Hansen Ethan Hansen - 1 month ago 18
Swift Question

How to update NSLayoutConstraint constants, that have previously been set in code? - Swift

I'm trying to completely make my view programatically. It has a bottom view that animates up when a search query finishes with the relevant information. I've set the .bottom nslayoutconstraint of the bottom view to an optional nslayoutconstraint, first initialising it off screen in override func viewWillLayoutSubviews().

let bottomView = UIView()
var bottomViewBottomConstraint: NSLayoutConstraint!

override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(bottomView)

}

override func viewWillLayoutSubviews() {

//Bottom View Constraints
bottomView.translatesAutoresizingMaskIntoConstraints = false
let bottomViewLeftConstraint = NSLayoutConstraint(item: bottomView, attribute: .left, relatedBy: .equal, toItem: view, attribute: .left, multiplier: 1, constant: 0)
let bottomViewRightConstraint = NSLayoutConstraint(item: bottomView, attribute: .right, relatedBy: .equal, toItem: view, attribute: .right, multiplier: 1, constant: 0)
let bottomViewHeightConstraint = NSLayoutConstraint(item: bottomView, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 75)
bottomViewBottomConstraint = NSLayoutConstraint(item: self.bottomView, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 100)

NSLayoutConstraint.activate([
bottomViewLeftConstraint, bottomViewRightConstraint, bottomViewBottomConstraint, bottomViewHeightConstraint
])
}


The view appears up once the search query is complete.

self.view.layoutIfNeeded()

UIView.animate(withDuration: 0.8, delay: 1, usingSpringWithDamping: 1, initialSpringVelocity: 0.1, options: .curveEaseIn, animations: {
self.bottomViewBottomConstraint.constant = -5
self.view.layoutIfNeeded()
}, completion: nil)


However after that point, when I try to dismiss the view, and change the NsLayoutConstraint's constant, the view doesn't move.

self.view.layoutIfNeeded()
UIView.animate(withDuration: 0.8, animations: {
self.bottomViewBottomConstraint.constant = 100
self.view.layoutIfNeeded()
})


In the output console, I'm getting this error and I'm not sure how to fix it.

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2016-10-11 14:50:01.768353 Green Homes Australia[973:232019] [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:0x17028d6b0 UIView:0x1049365b0.bottom == UIView:0x100b04e40.bottom - 5 (active)>",
"<NSLayoutConstraint:0x174681cc0 UIView:0x1049365b0.bottom == UIView:0x100b04e40.bottom + 100 (active)>")

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x174681cc0 UIView:0x1049365b0.bottom == UIView:0x100b04e40.bottom + 100 (active)>

Answer

The problem is that your

override func viewWillLayoutSubviews() {

runs again and again and again. Layout happens a lot! Thus, those constraints all get created and added again and again, including after you have done the animation and changed the bottom constraint. So you end up with two conflicting bottom constraints. (You actually have lots of bottom constraints, but all the others are equal to one of the the conflicting constraints so the runtime doesn't complain.)

A simple solution that I like to use is to put a boolean flag so that my initial configuration only happens once:

var didConfig = false
override func viewWillLayoutSubviews() {
    if !didConfig {
        didConfig = true
        // create and configure constraints here
    }
}
Comments