我正在努力更好地了解容器视图在故事板中的工作原理。行为似乎是容器视图将强制其子视图调整大小以填充容器。
我看不到解释它的约束,也没有提到它是什么类。这似乎是一些故事板的魔力。
我假设容器视图必须是UIView
的子类,我会猜测并假设它被称为UIContainerView
,但搜索文档只会产生两个结果。
那它是如何运作的?
答案 0 :(得分:27)
故事板编辑器(Interface Builder)在编辑期间将嵌入视图的frame
设置为容器视图的bounds
。因此,当故事板被写入文件时,视图的序列化大小是相同的。无论故事板是否启用了自动布局,都会发生这种情况。
故事板中每个视图控制器的顶级视图也将其自动调整遮罩设置为UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
,无论故事板是否启用了自动布局。
如果启用了自动布局,则每个顶级视图的translatesAutoresizingMaskToConstraints
都设置为YES
。这与那些顶级视图的所有后代不同。所有后代都将translatesAutoresizingMaskToConstraints
设置为NO
。
嵌入关系表示为类UIStoryboardEmbedSegue
的segue。 (这是私有类,不属于公共API。)
当UIStoryboardEmbedSegue
收到perform
消息时,它会加载目标视图控制器的视图并将其添加为容器视图的子视图。然后,它将嵌入视图的autoresizingMask
设置为UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight
。这是多余的,因为Interface Builder已经在故事板中设置了它。
然后-[UIStoryboardEmbedSegue perform]
检查嵌入视图的translatesAutoresizingMaskToConstraints
。这也是多余的,因为Interface Builder将其设置为YES
。
如果嵌入视图的translatesAutoresizingMaskToConstraints
为YES
,则perform
会将嵌入视图的frame
设置为容器视图的bounds
。再次,冗余。
如果嵌入视图的translatesAutoresizingMaskToConstraints
为NO
,则perform
会添加约束H:|[childView]|
和V:|[childView]|
,从而强制嵌入视图填充容器视图。 (是的,它实际上使用了可视化格式语言。)不应该到达这个分支。
当视图将translatesAutoresizingMaskToConstraints
设置为YES
时,自动布局会自动添加NSAutoresizingMaskLayoutConstraint
类型的约束,并在您更改视图frame
时使其保持最新。例如,使用自动调整约束来使窗口的根视图填充窗口:
<NSAutoresizingMaskLayoutConstraint:0x7555d00 h=-&- v=-&- UIView:0x7671780.midX == UIWindow:0x7551010.midX>,
<NSAutoresizingMaskLayoutConstraint:0x7555de0 h=-&- v=-&- UIView:0x7671780.width == UIWindow:0x7551010.width>,
<NSAutoresizingMaskLayoutConstraint:0x7555eb0 h=-&- v=-&- UIView:0x7671780.midY == UIWindow:0x7551010.midY + 10>,
<NSAutoresizingMaskLayoutConstraint:0x7555ef0 h=-&- v=-&- UIView:0x7671780.height == UIWindow:0x7551010.height - 20>
这就是“导致容器视图保持其子视图的框架与其边界匹配”的原因。
我通过查看.storyboard
文件(令人惊讶的可读XML)以及查看Hopper中的-[UIStoryboardEmbedSegue perform]
来解决这个问题。
至于他们为什么要进行冗余检查,我可以想到几个可能的原因:
IB(可能在预发布版本中)并不总是像现在一样设置视图属性,因此在加载旧故事板时代码不会多余。
Apple拥有的内部工具可以生成与IB不同的故事板。
代码用于向前兼容IB的未来版本,允许顶级故事板视图具有不同的属性。
答案 1 :(得分:0)
设置测试项目并单步执行代码可以让您清楚地了解如何实现它。容器视图本身是一个简单的UIView。作为控制器loadView进程的一部分,使用故事板中的约束设置创建容器视图。然后执行嵌入式控制器segue。这将创建子视图控制器,该视图将其视图添加为容器视图的子视图,并设置适当的布局约束,以便子视图填充容器。多数民众赞成真的。