是啥导致 Container View 保持其子视图的框架与其边界匹配?

Posted

技术标签:

【中文标题】是啥导致 Container View 保持其子视图的框架与其边界匹配?【英文标题】:What causes Container View to keep its child view's frame to match its bounds?是什么导致 Container View 保持其子视图的框架与其边界匹配? 【发布时间】:2013-06-30 02:22:23 【问题描述】:

我试图更好地理解容器视图在故事板中的工作方式。行为似乎是容器视图将强制其子视图调整大小以填充容器。

我没有看到可以解释它的约束,也没有提到它是什么类。这似乎是一些故事板魔术。

我假设容器视图必须是UIView 的子类,我会大胆猜测并假设它的名称为UIContainerView,但搜索文档只会产生两个结果。

那么它是如何工作的呢?

【问题讨论】:

我不认为它是一个子类,我认为它只是一个 UIView,并且在幕后,Xcode 将嵌入式控制器的框架设置为等于容器视图的边界。 【参考方案1】:

情节提要编辑器(界面生成器)在编辑期间将嵌入视图的 frame 设置为容器视图的 bounds。因此,当故事板被写入文件时,视图的序列化大小是相同的。无论故事板是否启用了自动布局,都会发生这种情况。

故事板中每个视图控制器的***视图也将其自动调整大小掩码设置为UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight,同样无论故事板是否启用了自动布局。

如果启用了自动布局,则每个***视图都将其translatesAutoresizingMaskToConstraints 设置为YES。这与那些***视图的所有后代不同。所有后代都将translatesAutoresizingMaskToConstraints 设置为NO

嵌入关系表示为UIStoryboardEmbedSegue 类的segue。 (这是一个私有类,不是公共 API 的一部分。)

UIStoryboardEmbedSegue 收到perform 消息时,它会加载目标视图控制器的视图并将其添加为容器视图的子视图。然后它将嵌入视图的autoresizingMask 设置为UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight。这是多余的,因为 Interface Builder 已经在情节提要中这样设置了。

然后-[UIStoryboardEmbedSegue perform] 检查嵌入视图的translatesAutoresizingMaskToConstraints。这也是多余的,因为 Interface Builder 将其设置为 YES

如果嵌入视图的translatesAutoresizingMaskToConstraintsYESperform 将嵌入视图的frame 设置为容器视图的bounds。再次,多余的。

如果嵌入视图的translatesAutoresizingMaskToConstraintsNOperform 添加约束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>

这就是“导致 Container View 保持其子视图的框架与其边界匹配”的原因。

我通过查看 .storyboard 文件(它的可读性惊人的 XML)和查看 Hopper 中的 -[UIStoryboardEmbedSegue perform] 发现了这一点。

至于为什么他们有多余的检查,我可以想到几个可能的原因:

    IB(可能在预发布版本中)并不总是像现在这样设置视图属性,因此在加载旧情节提要时代码不是多余的。

    Apple 的内部工具生成故事板的方式与 IB 不同。

    该代码用于向前兼容 IB 的未来版本,允许***故事板视图具有不同的属性。

【讨论】:

很好的解释!这是否也意味着,在使用情节提要时,不可能让容器视图的内容决定容器视图的高度(在它嵌入的视图中应用了使视图居中的约束)? @Scott 听起来你应该阅读我对this question 的回答。【参考方案2】:

设置一个测试项目并逐步执行代码可以让您相当清楚地了解它是如何实现的。容器视图本身是一个普通的 UIView。作为控制器 loadView 过程的一部分,容器视图是使用情节提要中的约束设置创建的。然后执行嵌入式控制器segue。这将创建子视图控制器,该控制器将其视图添加为容器视图的子视图,并设置适当的布局约束,以便子视图填充容器。真的是这样。

【讨论】:

是的,但显然它不起作用。调整 ContainerView 后,子视图不适合新的边界。

以上是关于是啥导致 Container View 保持其子视图的框架与其边界匹配?的主要内容,如果未能解决你的问题,请参考以下文章

是啥导致 SwiftUI 嵌套的 View 项目在初始绘制后出现跳跃动画?

android-ImageView及其子类

uni-app基础组件—视图容器(View Container)

从其子 UIViewControllers 访问父 UIViewController

Angular 10 ng-content 投影导致表格冻结

SwiftUI - 知道以前的视图是啥