将子类化的UIView与其自动布局约束添加到Nib中

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

使用Autolayout Constraints设置View B,包括约束条件,并在Interface Builder中制作。将A添加到viewController的Nib中。

B - UIImageView(Leading = 10,Trailing = 10,AlignVertically) - UITextField(Leading = 10,Trailing = 10,AlignVertically)

ViewController A(300x300,AlignHorizontally,AlignVertically)

在ViewController中,我将A固定为300x300, B1和B2的Leading,Trailing,Top和Bottom固定为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的AutoLayout约束。

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

PS:View A 也曾经是自定义的。

PPS:我使用Swift来完成此操作,但我相信Objective-C的解决方案也适用。


看一下这个教程,它应该有助于在Swift中使用编程创建的约束:http://makeapppie.com/2014/07/26/the-swift-swift-tutorial-how-to-use-uiviews-with-auto-layout-programmatically/ - LS_
2个回答

5
首先,你遇到的错误是因为:
  • 你不能在视图之间交换或移动约束
  • 视图必须在添加新约束之前已经添加到层次结构中
  • 通常会在包含视图(或公共祖先)的父视图上添加约束。

    (有关更多详细信息,请参见AutoLayout指南

我建议使用视图A(CustomView:UIView)从nib文件加载内容(B Views)并将它们添加为其子视图。
这个技巧是,您需要在内容视图中布置B视图,以便您的nib文件只有一个根对象。
这样,当您将内容视图作为视图A的子视图添加时,所有约束都会传递过去(与UITableViewCells如何使用其contentView类似)。

重要提示:内容视图不应该是CustomView类类型。如果你想拖出口和操作,只需声明File's Owner对象的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()
}

}

我尝试了多种手动添加嵌入视图约束的方法,但没有一种方法像这种方式那样可靠 - 翻译桅杆并打开 translatesAutoresizingMaskIntoConstraints。 - Kendall Helmstetter Gelner

4
问题是:从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的代码,我把它从这个答案中复制过来了 :)


太棒了。我永远不会想到如何正确地从self复制这些约束。 - zmonteca

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