I have a UIImageView
embedded in a UIView
. My entire app uses AutoLayout
, but I want to remove constraints
for the
There are two distinct issues here:
Regarding (1), jturton is correct. As long as you have enabled auto layout within Xcode, Xcode will guarantee/require that there are sufficient constraints to uniquely determine the size and location of your view.
If you have not manually specified enough constraints in Xcode (via the "user constraints" that appear in blue in the layout inspector), then Xcode will add new constraints that it guesses (which appear in purple) until there are sufficient constraints to determine the layout.
If the UIImageView had no constraints on it, its layout would not be determined, so you cannot set that up in Xcode. To fix this, what you should do is use Xcode to setup IBOutlets to all the constraints you want to remove, and then in your UIView's awakeFromNib: or in your UIViewController's viewDidLoad: you manually remove the constraints by calling removeConstraints:.
However, now your UIImageView's layout is underdetermined. So at this point you will want to manually add new constraints that will allow you to move and resize the UIImageView, which brings us to question (2).
Regarding (2), how to resize & move a view whose layout is determined by constraints, the answer is to setup gesture recognizers as usual to detect the pinch & pan gestures, but instead of those gesture recognizers calling setFrame: to modify the view, they should modify the constant
parameter of a NSLayoutConstraint that determines the frame of the UIImageView and then call layoutIfNeeded:, which will cause the layout system to immediately translate that modified constraint into a new frame.
So, for instance, suppose you want to manipulate the UIImageView with a set of layout constraints that were very similar to familiar setFrame: calls. In that case, after removing the constraints setup by Xcode, you could add constraints to the view that specify its width, its height, and its space from the top and left of the superview. Then the action handler for your pan gesture recognizer, would just update the constant parameter of the pacer gesture recognizer, and your pinch gesture recognizer could just update the constant parameter of the height and width constraints.
Another way to do (2), which is the same under the hood but might be easier in practice, is to set translatesAutoresizingMaskIntoConstraints=YES
. This will do two things. It will cause the auto layout system to automatically generate constraints in the view's superview based on the view's autoresizingMask. Second – crucially! – it will cause the layout system to automatically translate calls to setFrame: into modifications of those constraints.
If you simply want to remove a view from a hierarchy, just call [theView removeFromSuperview]
. Any constraints affecting it will also be removed.
However, sometimes you might also want to put a view back, having temporarily removed it.
To achieve this, I use the following category on UIView
. It walks up the view hierarchy from a view v
to some ultimate container view c
(usually your view controller's root view) and removes constraints that are external to v
. These are constraints that are siblings to v
, rather than constraints that only affect v
's children. You can hide v
.
-(NSArray*) stealExternalConstraintsUpToView: (UIView*) superview
{
NSMutableArray *const constraints = [NSMutableArray new];
UIView *searchView = self;
while(searchView.superview && searchView!= superview)
{
searchView = searchView.superview;
NSArray *const affectingConstraints =
[searchView.constraints filteredArrayUsingPredicate:
[NSPredicate predicateWithBlock:^BOOL(NSLayoutConstraint *constraint, NSDictionary *bindings)
{
return constraint.firstItem == self || constraint.secondItem == self;
}]];
[searchView removeConstraints: affectingConstraints];
[constraints addObjectsFromArray: affectingConstraints];
}
return constraints;
}
The method passes back the removed constraints in an array that you can keep somewhere so that later on you can add the constraints back.
Use it a bit like this…
NSArray *const removedConstraints = [viewToHide stealExternalConstraintsUpToView: self.view];
[viewToHide setHidden: YES];
And later on, put the constraints back.
As of iOS 8 you don't need to think about where the constraints should be put. Just use this…
[NSLayoutConstraint activateConstraints: removedConstraints];
[viewToHide setHidden: NO];
With earlier versions of iOS you don't need to think hard about where to add the constraints back. Throw then in the controller's self.view
. Provided constraints are in a superview of all the views that they affect, they will work.
[self.view addConstraints: removedConstraints];
[viewToHide setHidden: NO];
I agree Xcode auto-layout is horrible to use if you want something particular...
What I use to remove constraints programatically:
[detailsPhotoView removeConstraints:detailsPhotoView.constraints];
You can't not have constraints - you must have enough constraints in place to unambiguously size and position every view
You can create a resizable view that is laid out using Autolayout.
For example, in your case, you can create positioning constraints - say, pin the centre of the image view horizontally and vertically in the centre of its superview.
You can also create size constraints, horizontally and vertically. See my answer here for how to do this. Create outlets to these two constraints (if using interface builder). You can have height and width constraints of a specific number, or use the multiplier to make a fixed aspect ratio.
In response to pinch gestures, you can adjust the .constant
properties of the two size constraints, and call setNeedsLayout
. This will resize your image view whilst keeping its centre pinned.
If you do the fixed aspect ratio suggestion above, resizing a view using constraints is actually much simpler than setting the frame.
you can call
[yourElement removeFromSuperview];
and then call
[yourSuperview addSubview:yourElement];
to remove all constraints from yourElement
and add it back to the view.
It is worth mentioning that if you are doing the above on something like a UIView category or extension, you need to save a reference to the view's superview before removing from superview:
var theSuperview = self.superview
self.removeFromSuperview()
theSuperview?.addSubview(self)
You might also need to make yourElement strong instead of weak since removing from superview could potentially cause you to lose a reference to it.
In XCode 5 there is an option while editing constraints 'Remove at build time'. If this is enabled the constraint will automatically be removed during run-time. The option is there to keep Interface Builder happy if you don't want particular constraints.