Centering subview's X in autolayout throws "not prepared for the constraint"
Solution 1
As the error states, you must add the constraint to a view such that the views involved in the constraint are the view you're adding it to or a subview of that view. In the case of your code above, you should be adding that constraint to self
, rather than [self internalView]
. The reason it doesn't work as written is one of the views involved in the constraint (self
) is not in the view hierarchy if you consider only [self internalView]
and below).
For more details, see the discussion section of the documentation on the addConstraint:
method.
Here is your line of code, fixed as I suggest:
UIView* internalView = [[UIView alloc] initWithFrame:CGRectZero];
internalView.translatesAutoresizingMaskIntoConstraints = NO;
[self setInternalView:internalView];
[self addConstraint: [NSLayoutConstraint constraintWithItem: [self internalMapView]
attribute: NSLayoutAttributeCenterX
relatedBy: NSLayoutRelationEqual
toItem: self
attribute: NSLayoutAttributeCenterX
multiplier: 1
constant: 0]];
Solution 2
For others who come here: I got this error, because I added the constraints before I added the subviews to the hierarchy.
Solution 3
Short answer: swap parent and child view.
Assuming self
is the parent view:
bad:
[subview addConstraint [NSLayoutConstraint constraintWithItem:self...
good:
[self addConstraint [NSLayoutConstraint constraintWithItem:subview...
Swift
subview.translatesAutoresizingMaskIntoConstraints = false
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
"H:|-0-[subview]-0-|",
options: .DirectionLeadingToTrailing,
metrics: nil,
views: ["subview":subview]))
Obj-C
[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addConstraints:[NSLayoutConstraint
constraintsWithVisualFormat:@"H:|-0-[subview]-0-|"
options:NSLayoutFormatDirectionLeadingToTrailing
metrics:nil
views:NSDictionaryOfVariableBindings(subview)]];
Solution 4
Additional Tips:
I have a newly developed App which running fine on iOS7 but get error on iOS8 "The view hierarchy is not prepared for the constraint".
Read some other post said that the (sub)views need to be added before create constraints. I did that but still got the error.
Then I figured out that I should create constraints under -(void)viewDidAppear instead of viewDidLoad
Solution 5
This is an old question but I want to point out this line from the docs:
When developing for iOS 8.0 or later, set the constraint’s isActive property to true instead of calling the addConstraint(_:) method directly. The isActive property automatically adds and removes the constraint from the correct view.
At the very least, this will remove one point of failure since you no longer need to worry about calling addConstraint on the wrong view
Related videos on Youtube
Comments
-
Patrick Perini almost 2 years
I have a custom UIView subclass which is being initialized via a nib.
In
-awakeFromNib
, I'm creating a subview and attempting to center it in its superview.[self setInteralView: [[UIView alloc] init]]; [[self internalView] addConstraint: [NSLayoutConstraint constraintWithItem: [self internalView] attribute: NSLayoutAttributeCenterX relatedBy: NSLayoutRelationEqual toItem: self attribute: NSLayoutAttributeCenterX multiplier: 1 constant: 0]];
This breaks, and causes the following output:
2013-08-11 17:58:29.628 MyApp[32414:a0b] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0xc1dcc80 UIView:0xc132a40.centerX == MyView:0xc1315a0.centerX> When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView _viewHierarchyUnpreparedForConstraint:] to debug. 2013-08-11 17:58:29.630 MyApp[32414:a0b] View hierarchy unprepared for constraint. Constraint: <NSLayoutConstraint:0xc1dcc80 UIView:0xc132a40.centerX == MyView:0xc1315a0.centerX> Container hierarchy: <UIView: 0xc132a40; frame = (0 0; 0 0); clipsToBounds = YES; layer = <CALayer: 0xc132bc0>> View not found in container hierarchy: <MyView: 0xc1315a0; frame = (-128 -118; 576 804); layer = <CALayer: 0xc131710>> That view's superview: <UIView: 0xc131a70; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0xc131b50>>
-
Patrick Perini over 10 yearsThat gives me an error claiming
Unable to simultaneously satisfy constraints. Probably at least one of the constraints in the following list is one you don't want.
And then it breaks<NSLayoutConstraint:0x126ad4c0 UIView:0xadd2e20.centerX == MyView:0xadd19b0.centerX>
-
Charles A. over 10 yearsMost likely because you didn't disable translation of auto resizing mask into constraints. I'll update my code above to do so. You'll probably also need to add width/height constraints for it to be visible, and I would recommend adding a constraint on center Y or some sort of top/bottom constraint to self to make the layout well defined.
-
Sam over 10 yearsso you cant have layout like:
@"V:|[v]|"
? as this would refer to a superview? -
Charles A. over 10 years@Sam That's a valid constraint, but it won't center the view represented by
[v]
, it will give you top and bottom constraints of the standard size between[v]
's top and bottom edges and that of its superview. You would also have to add it to the superview, I believe. -
3lvis about 10 yearsThanks for internalView.translatesAutoresizingMaskIntoConstraints = NO;
-
Deepak G M over 9 yearsA simple thumb rule : Any constraints that relates to the size of a view should be added to the same view and any constraints that relates to its position should be added to its parent.
-
Charles A. over 9 years@DeepakGM It is certainly possible to add a constraint related to something above the level of the parent, in which case that rule of thumb wouldn't work. The simple rule of thumb is to add the constraint to the common ancestor of the two views you're dealing with, or in the case of one view that view itself.
-
Teddy about 9 years@DeepakGM: that helped me
-
Sam Shaikh over 8 yearsit gives no any error but it does not change the position. why any one explain.
-
Stas almost 8 yearsThanks, you saved me a lot of time!
-
Ayan Sengupta almost 8 yearsThis is the most common reason for this failure I have noticed so far.
-
maganap almost 8 yearsWhen
viewDidAppear()
is called, the view is already visible to the user. If modifying constraints it should be before that, inviewWillLayoutSubviews()
. More of the lifecycle here: medium.com/@SergiGracia/… -
Nazir over 7 yearsexactly and when you do that
constraintWithItem: youView
the item is not exist in it! -
Nitesh Borad over 7 years@CharlesA.: What is difference between your code and OP's code apart from translatesAutoresizingMaskIntoConstraints = NO? And what is [self internalMapView]? Where does it come from? Your answer seems confusing to me. :(. Please elaborate more.
-
Charles A. over 7 years@NiteshBorad The only difference is in fact the translatesAutoresizingMaskIntoConstraints = NO statement, which disables the extra constraints that were causing the OP issues. The other code was from the OPs question - I do not know specifically what it does.
-
Nitesh Borad over 7 years@CharlesA.: Ok that's clear. But in "constraintWithItem: [self internalMapView]", shouldn't it be "internalView" in place of "internalMapView"? If not, what is "internalMapView"? None of you mentioned about it?
-
Charles A. over 7 years@NiteshBorad Assuming that the
[self setInternalMapView:internalView]
ensures that[self internalMapView]
returns what was last set, it would be the same reference regardless of if you provide[self internalMapView]
orinternalView
as the parameter. Technically it would be slightly more efficient to just passinternalView
as you wouldn't be invoking a method, but it is otherwise the same. -
Nitesh Borad over 7 years@CharlesA.: (y) Got it. Thanks.