Centering subview's X in autolayout throws "not prepared for the constraint"

53,923

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

Share:
53,923

Related videos on Youtube

Patrick Perini
Author by

Patrick Perini

coffee, curry, code, compassion

Updated on July 08, 2022

Comments

  • Patrick Perini
    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
    Patrick Perini over 10 years
    That 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.
    Charles A. over 10 years
    Most 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
    Sam over 10 years
    so you cant have layout like: @"V:|[v]|" ? as this would refer to a superview?
  • Charles A.
    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
    3lvis about 10 years
    Thanks for internalView.translatesAutoresizingMaskIntoConstraints = NO;
  • Deepak G M
    Deepak G M over 9 years
    A 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.
    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
    Teddy about 9 years
    @DeepakGM: that helped me
  • Sam Shaikh
    Sam Shaikh over 8 years
    it gives no any error but it does not change the position. why any one explain.
  • Stas
    Stas almost 8 years
    Thanks, you saved me a lot of time!
  • Ayan Sengupta
    Ayan Sengupta almost 8 years
    This is the most common reason for this failure I have noticed so far.
  • maganap
    maganap almost 8 years
    When viewDidAppear() is called, the view is already visible to the user. If modifying constraints it should be before that, in viewWillLayoutSubviews(). More of the lifecycle here: medium.com/@SergiGracia/…
  • Nazir
    Nazir over 7 years
    exactly and when you do that constraintWithItem: youView the item is not exist in it!
  • Nitesh Borad
    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.
    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
    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.
    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] or internalView as the parameter. Technically it would be slightly more efficient to just pass internalView as you wouldn't be invoking a method, but it is otherwise the same.
  • Nitesh Borad
    Nitesh Borad over 7 years
    @CharlesA.: (y) Got it. Thanks.