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

Posted

技术标签:

【中文标题】即使在添加子视图之后,视图层次结构也没有为约束做好准备【英文标题】:View Hierarchy Not Prepared for Constraints Even After Adding Subviews 【发布时间】:2015-10-06 10:02:18 【问题描述】:

我正在尝试实现一个集合视图,其中每个单元格都有可变数量的 UIButton 实例,水平放置的首尾相连。

因为按钮的数量是动态的,所以我在运行时实例化按钮并以编程方式添加自动布局约束(第一次尝试这样做)。

这是我的代码:

class MyCustomCell: UICollectionViewCell

    var buttonTitles:[String]?

    var buttons = [UIButton]()

    override func didMoveToSuperview()
    
        super.didMoveToSuperview()

        guard let titles = buttonTitles else 
            return
        

        for button in buttons 
            button.removeFromSuperview()
        

        buttons.removeAll()


        self.translatesAutoresizingMaskIntoConstraints = false


        // FIRST LOOP: CREATE/ADD BUTTONS:

        for (index, title) in titles.enumerate()

            let button = UIButton(type: UIButtonType.Custom)

            button.translatesAutoresizingMaskIntoConstraints = false

            button.setTitle(title, forState: .Normal)
            button.tag = index

            addSubview(button)
            buttons.append(button)
        

        // SECOND LOOP: ADD CONSTRAINTS:

        for (index, button) in buttons.enumerate()

            // Vertical Constaints (common to all buttons)

            button.addConstraint(
                NSLayoutConstraint(
                    item: self,
                    attribute: NSLayoutAttribute.TopMargin,
                    relatedBy: NSLayoutRelation.Equal,
                    toItem: button,
                    attribute: NSLayoutAttribute.Top,
                    multiplier: 1,
                    constant: 0
                )
            )

            button.addConstraint(
                NSLayoutConstraint(
                    item: self,
                    attribute: NSLayoutAttribute.BottomMargin,
                    relatedBy: NSLayoutRelation.Equal,
                    toItem: button,
                    attribute: NSLayoutAttribute.Bottom,
                    multiplier: 1,
                    constant: 0
                )
            )


            // Horizontal Constriants

            if index == 0 

                // First - Constrain left to parent (self):

                button.addConstraint(
                    NSLayoutConstraint(
                        item: self,
                        attribute: NSLayoutAttribute.LeftMargin,
                        relatedBy: NSLayoutRelation.Equal,
                        toItem: button,
                        attribute: NSLayoutAttribute.Left,
                        multiplier: 1,
                        constant: 0
                    )
                )
            
            else 
                // Not first - Constrain left to previous button:

                let previousButton = self.subviews[index - 1]

                button.addConstraint(
                    NSLayoutConstraint(
                        item: previousButton,
                        attribute: NSLayoutAttribute.LeftMargin,
                        relatedBy: NSLayoutRelation.Equal,
                        toItem: button,
                        attribute: NSLayoutAttribute.Left,
                        multiplier: 1,
                        constant: 0
                    )
                )

                if index == (titles.count - 1) 

                    // Last: Constrain right

                    button.addConstraint(
                        NSLayoutConstraint(
                            item: self,
                            attribute: NSLayoutAttribute.RightMargin,
                            relatedBy: NSLayoutRelation.Equal,
                            toItem: button,
                            attribute: NSLayoutAttribute.Right,
                            multiplier: 1,
                            constant: 0
                        )
                    )
                
            
        
    

当我尝试添加第一个约束时抛出了这个异常:

The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7f9702d19610 MyAppName.MyCustomCell:0x7f9703a690e0.topMargin == UIView:0x7f9702d163e0.top>
    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(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug.
2015-10-06 18:39:58.863 Meeting Notes[15219:1817298] View hierarchy unprepared for constraint.
    Constraint: <NSLayoutConstraint:0x7f9702d19610 MyAppName.MyCustomCell:0x7f9703a690e0.topMargin == UIView:0x7f9702d163e0.top>
    Container hierarchy: 
<UIView: 0x7f9702d163e0; frame = (0 0; 97 60); gestureRecognizers = <NSArray: 0x7f96fb64b5c0>; layer = <CALayer: 0x7f96fb606880>>
    View not found in container hierarchy: <MyAppName.MyCustomCell: 0x7f9703a690e0; baseClass = UICollectionViewCell; frame = (103 0; 97 60); clipsToBounds = YES; opaque = NO; layer = <CALayer: 0x7f96fb64bcd0>>
    That view's superview: <UICollectionView: 0x7f96fb8cc800; frame = (0 172; 1024 596); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x7f9701f813f0>; layer = <CALayer: 0x7f9701f865c0>; contentOffset: 0, 0; contentSize: 1024, 182> collection view layout: <UICollectionViewFlowLayout: 0x7f9701f8df50>
(lldb)

正如here 和其他类似问题所讨论的,当约束中涉及的视图不处于父子关系时,就会发生这种情况;但是,在添加任何约束之前,我已经添加了所有子视图(我将循环分成两个)。此外,整个代码在单元格的 didMovetoSuperview 内运行,因此单元格本身不是孤立的...

我错过了什么?

【问题讨论】:

【参考方案1】:

查看错误信息:

添加到视图时,约束项必须是该视图(或视图本身)的后代

在您的情况下,应将所有约束添加到self

self.addConstraint( ...

【讨论】:

这个参数旁边的item应该和to item交换 谢谢,我误读为“一个必须是另一个的子视图”。那么,如何在按钮之间添加约束? self.addConstraint(NSLayoutConstraint(item: button1, ... toItem: button2 ...)) 应该可以工作。 不再崩溃,谢谢。但是由于某种原因,单元格没有显示。现在会调查...

以上是关于即使在添加子视图之后,视图层次结构也没有为约束做好准备的主要内容,如果未能解决你的问题,请参考以下文章

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

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

在扩展中出现错误“视图层次结构没有为约束做好准备”

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

“视图层次结构没有为约束做好准备”错误 Swift 3

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