Centering subview's X in autolayout throws “no

2019-01-07 09:36发布

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>>

7条回答
Deceive 欺骗
2楼-- · 2019-01-07 10:05

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]];
查看更多
不美不萌又怎样
3楼-- · 2019-01-07 10:11

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)]];
查看更多
劫难
4楼-- · 2019-01-07 10:13

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

查看更多
兄弟一词,经得起流年.
5楼-- · 2019-01-07 10:21

In my case, it was solved by using root view as a container of constraints instead of a currently created view.

I mean, use inside viewController

view.addConstraints([leftConstraintImage, topConstraintImage]) instead of imageView.addConstraints...

查看更多
贼婆χ
6楼-- · 2019-01-07 10:27

For others who come here: I got this error, because I added the constraints before I added the subviews to the hierarchy.

查看更多
beautiful°
7楼-- · 2019-01-07 10:27

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

查看更多
登录 后发表回答