在自动布局中,将子视图的X居中会抛出“未准备好约束”的错误。

83

我有一个自定义的UIView子类,通过nib进行初始化。

-awakeFromNib中,我正在创建一个子视图并尝试将其居中在其父视图中。

[self setInteralView: [[UIView alloc] init]];
[[self internalView] addConstraint: [NSLayoutConstraint constraintWithItem: [self internalView]
                                                                 attribute: NSLayoutAttributeCenterX
                                                                 relatedBy: NSLayoutRelationEqual
                                                                    toItem: self
                                                                 attribute: NSLayoutAttributeCenterX
                                                                multiplier: 1
                                                                  constant: 0]];

这会导致代码出错,并且会输出以下内容:

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>>
8个回答

117

对于其他来到这里的人:我遇到了这个错误,因为我在将子视图添加到层次结构之前添加了约束。


谢谢,你节省了我很多时间! - Stas
到目前为止,这是我注意到的最常见的失败原因。 - Ayan Sengupta
当你执行 constraintWithItem: youView 时,其中的项目并不存在! - Nazir

96
正如错误所述,您必须将约束添加到一个视图中,使得涉及约束的视图是您要添加约束的视图或该视图的子视图。在上面的代码中,您应该将该约束添加到self而不是[self internalView]。之所以它不能像编写的那样工作,是因为约束中涉及的视图(self)不在视图层次结构中,如果仅考虑[self internalView]及以下内容的话。
有关更多详细信息,请参见addConstraint:方法的文档讨论部分
这是按照我的建议修复的您的代码行:
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]];

1
很可能是因为你没有禁用自动调整大小蒙版转换为约束条件。我会更新我的上面的代码来做到这一点。您可能还需要添加宽度/高度约束条件才能使其可见,并且我建议在自身上添加一个Y中心的约束条件或某种顶部/底部约束条件,以使布局得到良好定义。 - Charles A.
所以您不能有以下这样的布局:@"V:|[v]|"吗?因为这将会使用父视图作为参照? - Sam
1
@Sam 这是一个有效的限制条件,但它不会将 [v] 表示的视图居中,它将为 [v] 的顶部和底部边缘与其父视图之间的标准大小添加上下约束。我认为您还需要将其添加到父视图中。 - Charles A.
感谢使用 internalView.translatesAutoresizingMaskIntoConstraints = NO; - 3lvis
8
一个简单的拇指规则:任何与视图大小相关的限制应该添加到相同的视图中,而任何与其位置相关的限制应该添加到其父视图中。 - Deepak G M
显示剩余8条评论

36
< p >简短回答:交换父视图和子视图。好的代码示例中使用了self作为父视图:

  • 不好的示例:[subview addConstraint [NSLayoutConstraint constraintWithItem:self...

  • 好的示例:[self addConstraint [NSLayoutConstraint constraintWithItem:subview...


Swift

subview.translatesAutoresizingMaskIntoConstraints = false
self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(
    "H:|-0-[subview]-0-|",
    options: .DirectionLeadingToTrailing,
    metrics: nil,
    views: ["subview":subview]))

Objective-C

[subview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addConstraints:[NSLayoutConstraint
                      constraintsWithVisualFormat:@"H:|-0-[subview]-0-|"
                      options:NSLayoutFormatDirectionLeadingToTrailing
                      metrics:nil
                      views:NSDictionaryOfVariableBindings(subview)]];

10

额外提示:

我有一个新开发的应用,在iOS7上运行良好,但在iOS8上会出现错误:“视图层次结构未准备好进行约束”。

阅读了一些其他帖子说,需要在创建约束之前添加(子)视图。我做到了,但仍然出现了错误。

后来我发现我应该在-(void)viewDidAppear下创建约束,而不是在viewDidLoad下创建


2
当调用viewDidAppear()时,视图已经对用户可见。如果要修改约束,则应在此之前,在viewWillLayoutSubviews()中进行。更多关于生命周期的信息请参见:https://medium.com/@SergiGracia/ios-uiviewcontroller-lifecycle-261e3e2f6133#.8q888tocb - maganap

6

这是一个老问题,但我想指出文档中的这一行:

在开发iOS 8.0或更高版本时,将约束的isActive属性设置为true,而不是直接调用addConstraint(_:)方法。isActive属性会自动将约束添加到正确的视图中并从中删除。

至少,这将消除一个故障点,因为您不再需要担心在错误的视图上调用addConstraint。


2
在我的情况下,解决方法是使用根视图作为约束的容器,而不是当前创建的视图。
我是说,在viewController内部使用:
``` view.addConstraints([leftConstraintImage, topConstraintImage]) ```
而不是
``` imageView.addConstraints... ```

1
正如 @CharlesA 在他的回答中指出的那样,问题在于你添加的视图不在正确的视图层次结构中。 他的答案很好,但是我要为一个特定用例提供一些详细信息,因为这个问题也发生在我身上:
当我尝试添加约束到一个视图时,我的视图控制器是使用 instantiateViewControllerWithIdentifier 实例化的。为了解决这个问题,我使用了 [childViewController didMoveToParentViewController:self]。这将视图添加到了被我的约束引用的视图层次结构中。

0

首先添加您的子视图,然后像这样添加约束

addSubview(yourSubViewHere)
yourSubViewHere.translatesAutoresizingMaskIntoConstraints = false

addConstraint(NSLayoutConstraint(....

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接