Austin Berenyi Austin Berenyi - 24 days ago 8
Swift Question

Why am I unable to install a constraint on my view?

I am trying to animate a button from outside the visible bounds of my

UIViewController
to the center of it. I have a constraint setup in my storyboard called
myButtonLeading
which I remove as part of my animation, and then I want to add a new constraint called
newCenterConstraint
.

@IBAction func updateDidTouch(_ sender: Any) {

UIView.animate(withDuration: 0.5, delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {

let newCenterConstraint = NSLayoutConstraint(item: self.myButton, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1.0, constant: 0.0)

self.myButton.removeConstraint(self.myButtonLeading)
self.myButton.addConstraint(newCenterConstraint)

self.view.layoutIfNeeded()

}, completion: nil)
}


The code I have right now is giving me the following error message regarding my reference to
toItem: view
.


Implicit use of 'self' in closure; use 'self.' to make capture
semantics explicit


But when I used
self.view
my app crashes with an error message saying:


Unable to install constraint on view. Does the constraint reference
something from outside the subtree of the view? That's illegal.


Where am I going wrong here in adding the new centerX constraint?

Answer Source

The error is very clear. You are adding a constraint referencing self.view to a subview of self.view.

To fix this replace this line:

self.myButton.addConstraint(newCenterConstraint)

With:

self.view.addConstraint(newCenterConstraint)

A better approach though, as suggested by Lou Franco, is to instead of animating the addition of a new constraint, changing the constant of the center x constraint and animating the layoutIfNeeded function. (For this you’d have to connect an outlet for the center x constraint.)

It would look like this:

UIView.animate(withDuration: 0.5, delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
  self.centerConstraint.constant = 0
  self.view.layoutIfNeeded()
}, completion: nil)