将子类 UIView 添加到具有自动布局约束的 Nib

Posted

技术标签:

【中文标题】将子类 UIView 添加到具有自动布局约束的 Nib【英文标题】:Adding a subclassed UIView to a Nib with its auto layout constraints 【发布时间】:2014-09-21 14:13:18 【问题描述】:

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

视图 B 是使用 Autolayout 约束设置并在 Interface Builder 中制作的,包括约束。在viewController的Nib中添加了A

B - UIImageView(前导 = 10,尾随 = 10,垂直对齐) - UITextField(Leading = 10, Trailing = 10, AlignVertically)

视图控制器 A(300x300,水平对齐,垂直对齐)

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

加载视图 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.

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

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

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

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

PS:View A 以前也是自定义的。

PPS:我正在使用 Swift 来执行此操作,但我确信 Objective-C 中的解决方案也可以。

【问题讨论】:

看看这个教程应该会有所帮助,以便在swift中使用以编程方式创建的约束makeapppie.com/2014/07/26/… 【参考方案1】:

首先你得到的错误是因为

您不能在视图之间交换或移动约束 在添加新约束之前,视图必须已经添加到层次结构中

约束通常添加在包含视图(或共同祖先)的父视图上。

(更多详情见AutoLayout Guide)

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

诀窍是您需要在 内容视图 中布置 B 视图,以便您的 nib 文件中只有一个根对象。

这样,当你将内容视图添加为视图A的子视图时,所有的约束都会转移过来(类似于UITableViewCells如何使用它们的contentView)。

重要提示:内容视图应该属于 CustomView 类类型。如果您想拖动出口和操作,只需声明类 CustomView 的 File's Owner 对象并将它们链接到那里。

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

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

这样,视图 A 在 ViewController 的故事板/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..【参考方案2】:

问题是:从self检索到的constraints指的是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 复制的 :)

【讨论】:

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

以上是关于将子类 UIView 添加到具有自动布局约束的 Nib的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式将自动布局约束添加到恒定宽度和高度的子视图

Swift UIView 小部件自动布局不起作用

UIView 不遵守 UIScrollView 中的自动布局约束

将带有自动布局的自定义 UIView 添加到 iCarousel

以编程方式在子类 UIView 的子视图上自动布局

使用自动布局约束以编程方式创建四个具有相同高度和宽度的 UIView