视图层次结构未准备好

Posted

技术标签:

【中文标题】视图层次结构未准备好【英文标题】:view hierarchy is not prepared 【发布时间】:2016-02-25 20:59:56 【问题描述】:

我是一名新手 Swift 开发人员,在一本优秀的 ios 9 Swift 书籍中遵循代码示例。这本书刚刚演示了如何在视图层次结构的两个不同分支中的两个对象(标签、按钮)之间添加约束(在代码中)。

myLabel 位于 viewBB 容器中,该容器位于顶部视图控制器中。 myButton 位于 viewBB 之外的顶部视图控制器中。

示例代码运行良好。但作为练习,我想在 viewBB 中创建第二个 label2,并将其位置限制为从 myLabel 偏移。所以两个标签(我认为)都在 viewBB 中。

在代码中创建 label2 并在代码中添加约束可以正常工作。

但是...如果我在界面构建器中创建 label2,并尝试移除 IB 约束并用我自己的约束替换它们,构建成功但应用程序崩溃并出现通常的“”错误。

*** 有趣的是错误信息谈到了一个标签和一个按钮,这表明本书的示例代码(我在下面将其称为 BLOCK 0)是违规者。

我做错了什么? (我已经阅读了我在错误消息中可以找到的所有内容,包括此论坛中的 4 个帖子。但我不知道为什么书籍代码失败了。)

错误信息:

2016-02-25 12:52:32.796 TwoViewConstraints[5713:1860768] 视图层次结构没有为约束准备: NSLayoutConstraint:0x7c9ce990 UILabel:0x7c9dbec0'Label'.centerX == UIButton:0x7c9deed0'Button'.centerX

当添加到视图时,约束的项必须是该视图(或视图本身)的后代。如果在组装视图层次结构之前需要解决约束,这将崩溃。中断 -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] 进行调试。

来自调试器的消息:由于信号 15 而终止

这是我的代码:

class ViewController: UIViewController 

@IBOutlet weak var myLabel: UILabel!
@IBOutlet weak var myButton: UIButton!
@IBOutlet weak var centerConstraint: NSLayoutConstraint!
@IBOutlet weak var viewBB: UIView!


@IBOutlet weak var lab2cxh: NSLayoutConstraint!
@IBOutlet weak var lab2cxv: NSLayoutConstraint!
@IBOutlet weak var label2: UILabel!


override func viewDidLoad() 
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    // BLOCK 0 - works fine, from the iOS9 book
    // myLabel and myButton were created with the interface builder
    // Goal is to remove an IB constraint, and replace it with one
    // that is built in code. (Because label and buttons are in 
    // different branches of the view hierarchy).
    //
    // remove auto center constraint from parent label
    viewBB.removeConstraint(centerConstraint)

    // center the label and the button across views
    let ccx = NSLayoutConstraint(item: myLabel,
        attribute: NSLayoutAttribute.CenterX,
        relatedBy: NSLayoutRelation.Equal,
        toItem: myButton,
        attribute: NSLayoutAttribute.CenterX,
        multiplier: 1.0,
        constant: 0)
    self.view.addConstraint(ccx)

//        BLOCK 1 - Create a second label object in code
//        this code works fine with the two added constraints below
//        create a second label object and add it to the view
//        let label2 = UILabel()
//        label2.translatesAutoresizingMaskIntoConstraints = false
//        label2.text="Label2"
//        viewBB.addSubview(label2)

    // BLOCK 2 - create a second label object in the interface builder
    // Suggested horiz/vert constraints are set in the IB.
    // So now I want to remove them, and replace them with my own,
    // as was done in the Swift book example code in BLOCK 0.
    // remove original label 2 constraints
    viewBB.removeConstraint(lab2cxv)
    viewBB.removeConstraint(lab2cxh)

    // adding these two constraints works fine when the label2 object
    // is created in code. But when I create label2 in the interface
    // builder and try to remove the IB constraints (as was done in the
    // first block of code), I get the error:
    //
    // ... "The view hierarchy is not prepared
    // for the constraint "Label.CenterX to Button.CenterX", which is 
    // the first block of code.
    //
    // NOTE** To me, it looks like the error is coming from the 
    // BLOCK 0 constraint, since it cites a label and a button, not
    // two labels. 
    // What am I doing wrong?
    let c2h = NSLayoutConstraint(item: label2,
        attribute: NSLayoutAttribute.CenterY,
        relatedBy: NSLayoutRelation.Equal,
        toItem: myLabel,
        attribute: NSLayoutAttribute.CenterY,
        multiplier: 1.0,
        constant: -50)
    self.viewBB.addConstraint(c2h)

    // constrain label 2 to be diagonally left of label one
    let c2v = NSLayoutConstraint(item: label2,
        attribute: NSLayoutAttribute.CenterX,
        relatedBy: NSLayoutRelation.Equal,
        toItem: myLabel,
        attribute: NSLayoutAttribute.CenterX,
        multiplier: 1.0,
        constant: -100)
    self.viewBB.addConstraint(c2v)



【问题讨论】:

尝试使用ccx.active = true 而不是self.view.addConstraint(ccx)。并为您的属性使用更好的名称。 感谢您回答我的问题。很抱歉,但建议的更改没有奏效。那行代码直接出自书本,在我在下面添加代码之前运行良好。 (当然,cx = 约束,c = 中心,cxv = 垂直约束等对任何人都没有多大帮助。)是否有任何很好的列表来说明是什么让视图层次结构没有准备好,或者我可以学习和遵循的约定?谢谢 尝试将所有代码移出 viewDidLoad 并移入 viewWillAppear。视图层次结构在 viewDidLoad 中没有完全建立。 【参考方案1】:

我从头开始重新编写了整段代码,并且成功了。去搞清楚。这是正常工作的代码。据我所知,它几乎是相同的代码,只是在此处和那里进行了较小的名称更改。

我不知道为什么这个版本有效,而以前的版本没有。这让我想知道 Interface Builder 设置是否有所不同。

class ViewController: UIViewController 

@IBOutlet weak var viewbb: UIView!
@IBOutlet weak var myLabel: UILabel!
@IBOutlet weak var centerConstraint: NSLayoutConstraint!
@IBOutlet weak var myButton: UIButton!

@IBOutlet weak var label2: UILabel!
@IBOutlet weak var label2centerConstraint: NSLayoutConstraint!
@IBOutlet weak var label2Yconstraint: NSLayoutConstraint!

override func viewDidLoad() 
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    // remove the IB centering constraint, then add our own between label and button
    viewbb.removeConstraint(centerConstraint)

    // make a new constraint to center label over button
    let newcon = NSLayoutConstraint(item: myLabel,
        attribute: NSLayoutAttribute.CenterX,
        relatedBy: NSLayoutRelation.Equal,
        toItem: myButton,
        attribute: NSLayoutAttribute.CenterX,
        multiplier: 1.0,
        constant: 0)
    self.view.addConstraint(newcon)

    // remove the IB centering constraint, then add our own between label and button
    viewbb.removeConstraint(label2centerConstraint)
    viewbb.removeConstraint(label2Yconstraint)

    // make a new constraint to center label over button
    let newcon2 = NSLayoutConstraint(item: label2,
        attribute: NSLayoutAttribute.CenterX,
        relatedBy: NSLayoutRelation.Equal,
        toItem: myLabel,
        attribute: NSLayoutAttribute.CenterX,
        multiplier: 1.0,
        constant: -100) // left of mylabel

    // make a new constraint to center label over button
    let newcon3 = NSLayoutConstraint(item: label2,
        attribute: NSLayoutAttribute.CenterY,
        relatedBy: NSLayoutRelation.Equal,
        toItem: myLabel,
        attribute: NSLayoutAttribute.CenterY,
        multiplier: 1.0,
        constant: 0) // left of mylabel

    self.view.addConstraint(newcon3)
    self.view.addConstraint(newcon2)

【讨论】:

我也尝试将两个新约束添加到 self.viewbb 而不是 self.view,但没有任何区别。代码仍然运行良好。 (回想一下,viewbb 是父***视图中的视图容器。viewbb 容器包含两个标签视图,因此新约束可以放置在直接父视图(viewbb)或***视图(self.view)中.

以上是关于视图层次结构未准备好的主要内容,如果未能解决你的问题,请参考以下文章

视图层次结构没有为约束做好准备?

uiscrollview的自动布局,视图层次结构没有为约束做好准备

视图层次结构没有为约束做好准备:无法找到问题

视图层次结构没有为约束做好准备

即使在添加子视图之后,视图层次结构也没有为约束做好准备

Swift,约束,视图层次结构没有为约束做好准备