I have a UIView which is placed on the screen via several constraints. Some of the constraints are owned by the superview, others are owned by other ancestors (e.g. perhaps the view property of a UIViewController).
I want to remove all of these old constraints, and place it somewhere new using new constraints.
How can I do this without creating an IBOutlet for every single constraint and having to remember which view owns said constraint?
To elaborate, the naive approach would be to create a bunch of IBOutlets for each of the constraints, and would then involve calling code such as:
[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];
The problem with this code is that even in the simplest case, I would need to create 2 IBOutlets. For complex layouts, this could easily reach 4 or 8 required IBOutlets. Furthermore, I would need to ensure that my call to remove the constraint is being called on the proper view. For example, imagine that myViewsLeftConstraint
is owned by viewA
. If I were to accidentally call [self.view removeConstraint:self.myViewsLeftConstraint]
, nothing would happen.
Note: The method constraintsAffectingLayoutForAxis looks promising, but is intended for debugging purposes only.
Update: Many of the answers I am receiving deal with self.constraints
, self.superview.constraints
, or some variant of those. These solutions won't work since those methods return only the constraints owned by the view, not the ones affecting the view.
To clarify the problem with these solutions, consider this view hierarchy:
- Grandfather
- Father
- Me
- Son
- Daughter
- Brother
- Me
- Uncle
- Father
Now imagine we create the following constraints, and always attach them to their nearest common ancestor:
- C0: Me: same top as Son (owned by Me)
- C1: Me: width = 100 (owned by Me)
- C2: Me: same height as Brother (owned by Father)
- C3: Me: same top as Uncle (owned by Grandfather)
- C4: Me: same left as Grandfather (owned by Grandfather)
- C5: Brother: same left as Father (owned by Father)
- C6: Uncle: same left as Grandfather (owned by Grandfather)
- C7: Son: same left as Daughter (owned by Me)
Now imagine we want to remove all constraints affecting Me
. Any proper solution should remove [C0,C1,C2,C3,C4]
and nothing else.
If I use self.constraints
(where self is Me), I will get [C0,C1,C7]
, since those are the only constraints owned by Me. Obviously it wouldn't be enough to remove this since it is missing [C2,C3,C4]
. Furthermore, it is removing C7
unnecessarily.
If I use self.superview.constraints
(where self is Me), I will get [C2,C5]
, since those are the constraints owned by Father. Obviously we cannot remove all these since C5
is completely unrelated to Me
.
If I use grandfather.constraints
, I will get [C3,C4,C6]
. Again, we cannot remove all of these since C6
should remain intact.
The brute force approach is to loop over each of the view's ancestors (including itself), and seeing if firstItem
or secondItem
are the view itself; if so, remove that constraint. This will lead to a correct solution, returning [C0,C1,C2,C3,C4]
, and only those constraints.
However, I'm hoping there is a more elegant solution than having to loop through the entire list of ancestors.
The only solution I have found so far is to remove the view from its superview:
This looks like it removes all constraints affecting its layout and is ready to be added to a superview and have new constraints attached. However, it will incorrectly remove any subviews from the hierarchy as well, and get rid of
[C7]
incorrectly.Using a Reusable Sequence
I decided to approach this in a more 'reusable' way. Since finding all constraints affecting a view is the basis for all of the above, I decided to implement a custom sequence that returns them all for me, along with the owning views.
First thing to do is define an extension on
Arrays
ofNSLayoutConstraint
that returns all elements affecting a specific view.We then use that extension in a custom sequence that returns all constraints affecting that view, along with the views that actually own them (which can be anywhere up the view hierarchy)
Finally we declare an extension on
UIView
to expose all the constraints affecting it in a simple property that you can access with a simple for-each syntax.Now we can iterate all constraints affecting a view and do what we want with them...
List their identifiers...
Deactivate them...
Or remove them entirely...
Enjoy!
In Swift:
(As of July 31, 2017)
SWIFT 3
Objective C
This is the easiest way to quickly remove all constraints that exist on a UIView. Just be sure to add the UIView back with it's new constraints or new frame afterwards =)
You could use something like this:
Basically, this is enumerates over all the constraints on the superview of viewA and removes all of the constraints that are related to viewA.
Then, the second part removes the constraints from viewA using the array of viewA's constraints.
I use the following method to remove all constraints from a view:
.h file:
.m file:
You can also read this post on my blog to better understand how it works behind the hood.
If you need more granular control, I'd strongly advise switching to Masonry, a powerful framework class you could use whenever you need to properly handle constraints programmatically.