50

I have an IBOutlet NSLayoutConstraint in my app. Very simple:

@property (nonatomic, weak) IBOutlet NSLayoutConstraint* leftConstraint;

At some point I want to deactivate this constraint:

self.leftConstraint.active = NO;

It happens in a method called from cellForRowAtIndexPath:. And the constraint becomes nil right after the line above. However if I declare this property as strong then it's ok, it doesn't turn to nil. Can anybody explain why it happens?

Andrey Chernukha
  • 21,488
  • 17
  • 97
  • 161

3 Answers3

100

When your outlet is weak, the only strong reference is from the view's constraints property. Deactivating the constraint removes it from that array, so there are no more strong references.

Avi
  • 7,469
  • 2
  • 21
  • 22
  • Elementary, Watson))) Thanks a lot! – Andrey Chernukha Jun 27 '16 at 10:57
  • 4
    The same conclusion I reached, but is it safe to make it strong? My constraint is in a collection view cell and I'm concerned that holding the strong ref will create a retain cycle when cells are recycled. – Echelon Sep 27 '16 at 14:08
  • @Echelon, that can't be answered without knowing more about your class relationships. However, cycles are OK as long as there's a clear point where they are broken. – Avi Sep 27 '16 at 17:23
  • One solution could be to change the `constant` value of the constraint. – Manish Singh Oct 23 '19 at 19:40
  • 1
    @Echelon I believe, yes, it is safe to make an `NSAutolayoutConstraint` `strong`. It's my understanding that `NSAutolayoutConstraint` itself has `weak` references to the elements that it constrains, so there is no possibility of a cycle. I'm happy to be corrected on this though! It seems like a question that could be resolved with a fairly simple unit test. Create two views. Create a strong constraint on them. Release your strong references on the views. Check if a weak reference on the views still reaches them. If it does, the constraint **is** holding them, otherwise, it's not. – Benjohn Nov 12 '20 at 09:56
  • https://stackoverflow.com/questions/27494542/when-can-i-activate-deactivate-layout-constraints It's working with clear explanation. – Lokesh Jul 12 '23 at 07:56
21

A possible workaround would be to change the constraint priority instead of making it inactive:

@property (nonatomic, weak) IBOutlet NSLayoutConstraint* leftConstraint;
(...)
self.leftConstraint.priority = 250;

Then, self.leftConstraint is not disposed.

EDIT:

Xcode does not support changing priority for required (=1000) constraints, so be sure you switch in a range between 1...999.

Fran Pugl
  • 464
  • 5
  • 6
8

First change the IBOutlet variable to an optional. ie:

from :

@IBOutlet weak var myConstraint : NSLayoutConstraint!

to:

@IBOutlet weak var myConstraint : NSLayoutConstraint?

Now, to make it easier to manage/change from the storyboard in the future, add a new variable (eg myConstraint_DefualtValue) and set this to the value of myConstraint in viewDidLoad

var myConstraint_DefualtValue = CGFloat(0)
override func viewDidLoad() {
    super.viewDidLoad()

    self.myConstraint_DefualtValue = self.myConstraint.constant

}

I assume it is obvious why you need to set it in viewDidLoad and not somewhere else

Then when you want to deactivate it:

self.myConstraint?.isActive = false

And when you want to reactivate it (assuming you have the view as an outlet(myViewThatHasTheConstraint) and the constraint is a height constraint):

self.myConstraint = self.myViewThatHasTheConstraint.heightAnchor.constraint(equalToConstant: self.myConstraint_DefualtValue);
self.myConstraint?.isActive = true
Mjd Mz
  • 121
  • 1
  • 5