6

I try to reassign an referecend NSLayoutConstraint.

class ViewController: UIViewController {
    @IBOutlet weak var myConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()
        exchangeConstraint(&myConstraint)
    }
}

extension UIViewController {
    func exchangeConstraint(_ constraint: inout NSLayoutConstraint) {
        let spacing = constraint.constant
        view.removeConstraint(constraint)
        constraint = view.topAnchor.constraint(equalTo: anotherView.topAnchor, constant: spacing)
        view.addConstraint(constraint)
    }
}

But here it gives me the error:

exchangeConstraint(&myConstraint)
-------------------^
Cannot pass immutable value of type 'NSLayoutConstraint' as inout argument

What i don't understand is why it says immutable value, whereas the constraint is declared as a variable, not a constant.

Lukas Würzburger
  • 6,207
  • 7
  • 35
  • 68
  • Constraints don't work like that. You need to remove the old constraint and install a new one (you can modify the constant property but you don't need an inout parameter for that). You should have a very good reason to use inout parameters anyway. It is better for the function to return a new constraint if that is what you want. – Paulw11 Dec 06 '17 at 09:41
  • yes, currently to keep my code running i do it as you suggest. so `myConstraint = doSomething(myConstraint)`. I just don't like using the variable two times in a line for one thing. I hope for a cleaner way. – Lukas Würzburger Dec 06 '17 at 09:45
  • 2
    That is the cleaner way compared to using a side effect (inout parameter). As I said, you need to remove the existing constraint anyway before you add a new one, if you don't have the reference to the exisiting constraint, it will be hard to do that. – Paulw11 Dec 06 '17 at 09:49
  • 1
    `NSLayoutConstraint` is reference type anyway. Why do you pass it as an `inout` parameter? – vadian Dec 06 '17 at 10:12
  • Ok, i updated my example. I want to keep the constraint variable, but change its content. So i remove it from the view, assign a new value to the variable and add it again. – Lukas Würzburger Dec 06 '17 at 10:23
  • Not before a computer, so I can‘t check. But I suspect this is because the var is an implicitly unwrapped optional. Does something change if you declare the constraint explicitely optional? – Alfonso Dec 06 '17 at 21:17
  • To elaborate, easiest would probably be to change the parameter type of the exchangeConstraint func so it takes an optional. This should get rid of the implicit unwrap – Alfonso Dec 06 '17 at 21:27
  • Or you could call it as `exchangeConstraint(&myConstraint!)` – `myConstraint!` produces an l-value that can be used with `inout`. – Hamish Dec 07 '17 at 13:31

1 Answers1

1

I solved it by simply declaring the constraint parameter as an explicitly unwraped NSLayoutConstraint.

func exchangeConstraint(_ constraint: inout NSLayoutConstraint!) {
    ...
}

UPDATE

Here is a project where I use it: https://github.com/truffls/compatible-layout-anchors-ios

Lukas Würzburger
  • 6,207
  • 7
  • 35
  • 68