使用自动布局约束将子类UIView添加到Nib

时间:2014-09-21 14:13:19

标签: ios iphone interface-builder autolayout

我正在尝试创建一个包含2个自定义视图(B)的UIView(A)

使用Autolayout Constraints设置View B并在Interface Builder中进行设置,包括约束。在viewController的Nib中添加了一个

乙   - UIImageView(前导= 10,尾随= 10,AlignVertically)   - UITextField(前导= 10,尾随= 10,AlignVertically)

的ViewController A(300x300,AlignHorizo​​ntally,AlignVertically)

在ViewController中,我将A固定在300x300和 B1和B2的前导,尾随,顶部和底部固定为0.(这应该使B1和B2为300x150,如果我错过了什么,请原谅我)

加载View B时,我使用以下代码加载其Nib:

override func awakeAfterUsingCoder(aDecoder: NSCoder!) -> AnyObject! {
    if self.subviews.count == 0 {
        let bundle = NSBundle(forClass: self.dynamicType)
        var view = bundle.loadNibNamed("B", owner: nil, options: nil)[0] as B
        view.setTranslatesAutoresizingMaskIntoConstraints(false)
        let constraints = self.constraints()
        self.removeConstraints(constraints)
        view.addConstraints(constraints)
        return view
    }
    return self
}

但是当我尝试运行时,我收到以下警告,包括崩溃:

The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x7f897ad1acc0 V:[TestProject.B:0x7f897af73840(300)]>
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.

我也尝试将视图添加为View B的属性,并使用以下代码将其添加到B

NSBundle.mainBundle().loadNibNamed("B", owner: self, options: nil)
self.addSubview(self.viewOfB);

结果是视图被添加到viewController中,但它没有采用任何来自其自己的Nib的AutoLayoutConstraints。

现在我不知道如何将这个视图添加到viewController的视图中,包括约束。我究竟做错了什么?有更好的方法吗?

PS:视图A过去也是自定义的。

PPS:我使用Swift来做这件事,但我确信Objective-C中的解决方案也适用。

2 个答案:

答案 0 :(得分:5)

首先,你得到的错误是因为

  • 您无法在视图之间交换或移动约束
  • 在添加新约束之前,必须已在层次结构中添加视图
  • 约束通常会添加到包含视图(或共同祖先)的父视图中。

    (有关详细信息,请参阅AutoLayout Guide

我建议让视图A(CustomView:UIView)从nib文件加载内容(B视图)并将它们添加为它的子视图。

Main View with CustomView A

诀窍是你需要在内容视图中布局B视图,这样你的nib文件中只有一个根对象。

这样,当您将内容视图添加为视图A子视图时,所有约束都将转移(类似于UITableViewCells如何使用其contentView)。

ContentView xib

重要提示:内容视图应 NOT 属于类型CustomView。如果您想拖动出口和操作,只需声明CustomView类的文件所有者对象并将其链接到那里。

您的最终视图层次结构应如下所示:

MainView (ViewController's view)
  View A
    Content View
      B1 View
      B2 View

这样,视图A在ViewController的storyboard / xib中配置了它的布局,你B视图将使用与内容视图相关的nib文件中的约束。

所以唯一要做的就是确保内容视图与视图A的大小始终相同。

class CustomView: UIView
{
 @IBOutlet var _b1Label: UILabel!
 @IBOutlet var _b2Button: UIButton!

func loadContentView()
{
    let contentNib = UINib(nibName: "CustomView", bundle: nil)

    if let contentView = contentNib.instantiateWithOwner(self, options: nil).first as? UIView
    {
        self.addSubview(contentView)

        //  We could use autoresizing or manually setting some constraints here for the content view
        contentView.translatesAutoresizingMaskIntoConstraints = true
        contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
        contentView.frame = self.bounds
    }
}

override init(frame: CGRect)
{
    super.init(frame: frame)

    loadContentView()
}

required init(coder aDecoder: NSCoder)
{
    super.init(coder: aDecoder);

    loadContentView()
}

}

答案 1 :(得分:4)

问题是:从constraints检索到的self是指self

相反,您必须使用约束来引用view并使用相同的其他属性。像这样:

NSMutableArray *constraints = [NSMutableArray array];
for(NSLayoutConstraint *constraint in self.constraints) {
    id firstItem = constraint.firstItem;
    id secondItem = constraint.secondItem;
    if(firstItem == self) firstItem = view;
    if(secondItem == self) secondItem = view;
    [constraints addObject:[NSLayoutConstraint constraintWithItem:firstItem
                                                        attribute:constraint.firstAttribute
                                                        relatedBy:constraint.relation
                                                           toItem:secondItem
                                                        attribute:constraint.secondAttribute
                                                       multiplier:constraint.multiplier
                                                         constant:constraint.constant]];
}

/* if you also have to move subviews into `view`.
for(UIView *subview in self.subviews) {
    [view addSubview:subview];
}
*/

[view addConstraints:constraints];

而且,我认为你应该从self复制一些属性。

view.frame = self.frame;
view.autoresizingMask = self.autoresizingMask;
view.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;

对不起Obj-C代码,我是从this answer复制的:)