Autolayout - 拉伸视图以填充其父视图

时间:2014-10-04 22:53:06

标签: ios objective-c autolayout

我在使用autolayout尝试使用子视图填充视图时发现了一些非常奇怪的行为。这个想法非常简单:向视图添加一个子视图,并使其使用父视图的所有宽度。

NSDictionary *views = @{
                        @"subview":subView,
                        @"parent":self
                       };

这不起作用:

[self addConstraints:
      [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview]|" 
                                              options:0 
                                              metrics:nil 
                                                views:views]];

子视图不使用父视图的整个宽度。

但这有效:

[self addConstraints:
      [NSLayoutConstraint constraintsWithVisualFormat:@"H:|[subview(==parent)]|" 
                                              options:0 
                                              metrics:nil 
                                                views:views]];

我希望两者都能按预期工作。那么为什么第一个例子不起作用呢?这是Apple在以下技术说明中推荐的内容:

https://developer.apple.com/library/ios/technotes/tn2154/_index.html

编辑 :(删除无关信息)

3 个答案:

答案 0 :(得分:5)

以下是您为“第一个示例”发布的约束:

first case

以下是您为“第二个示例”发布的约束:

second case

我看到这些结构存在两个不同之处,我在图中用红色突出显示了这些结构:

  1. 第一个(损坏的)情况在近根UIView 0x8433f8f0上有一个约束(0x8433f8f0),将其宽度固定为320个点。这是多余的,因为底层视图被限制为每个160点,并且有足够的约束使这些较窄视图的所有祖先都是320点宽。

  2. 第二个(工作)情况有一个约束(0x7eb4a670),将近底UIView 0x7d5f3fa0的宽度固定为DetailWeatherView 0x7d5f3270的宽度。这个约束是多余的,因为3fa0的左边缘被固定到3270的左边缘,而3fa0的右边缘被约束到3fa0的右边缘。我假设(==parent)谓词添加了这个约束。

  3. 所以你可能会想,每个案例都有一个冗余约束,那又怎样呢?没什么大不了的,对吧?

    不完全。在第二种情况下,冗余约束是真正无害的。您可以更改WeatherTypeBoxViewAdditionalWeatherInfoBox之一(或两者)的宽度,并且您仍然可以获得唯一的解决方案。

    在第一种情况下,如果视图的宽度没有改变,则冗余约束只是无害的 。如果更改f8f0上的320宽度约束而不更改叶视图上的160宽度约束,则约束系统没有解决方案。 Autolayout将打破解决系统的一个约束,它可能会破坏320宽度约束。我们可以看到系统强加了320宽度约束及其UIView-Encapsulated-Layout-Width注释,以强制此视图层次结构符合某个容器的大小(可能是屏幕大小;可能是容器视图控制器的强制大小)

    我不知道为什么你的第一个案例有这个额外的顶级约束而你的第二个案例没有。我倾向于相信你改变了导致这种差异的其他因素,但自动布局是神秘的,我也不是100%相信。

    如果你的目标是让这个视图层次结构填充其容器,并保持叶子视图的宽度相等,那么去除叶子视图上的160宽度约束并在它们之间创建一个等宽度约束。

答案 1 :(得分:2)

编辑:正如Ken Thomases指出的那样,我在这里完全错了!

是的,您指定的两个约束系统都应该使parentsubview(或parentcontentView)具有相同的宽度。

所以我会问,你是否完全确定你所看到的是contentView无法填补父母的内容?您在破损案例中看到的可能是父实际上正在缩小以适应contentView,并且您没有注意到它,因为父级具有透明或不可见的背景?

要检查此项,请将contentViewparent设置为具有不同的不透明背景颜色。如果我错了,您将清楚地看到父项扩展的背景区域,其宽度大于contentView。如果我是对的,contentView将完全覆盖父宽度。

这就是为什么我(可能是擅自)质疑你所陈述的事实的原因:

日志输出的主要区别在于,对于破碎的情况,我们看到一个意外的约束,它将UIView保持为固定宽度为320:

<NSLayoutConstraint:0x8433f8f0 'UIView-Encapsulated-Layout-Width' H:[UIView:0x841b9f40(320)]>

那是什么观点?基于约束名称&#34; UIView-Encapsulated-Layout-Width&#34;,它与UIViewControllerWrapperView相关联,并且宽度保持为320(iPhone的宽度),我们可以推导出这个约束是由UIKit自动创建的,以将UICollectionView.view的大小限制为屏幕大小。查看所有约束,您可以进一步看到此width = 320约束通过一系列约束(encapsulated-view - &gt; DetailView - &gt; DetailWeatherView)向下传递,直到它最终确定DetalWeatherView的宽度为止,这是你的parent,它使用superview-edge-offset约束来拥抱contentView,因此也应该获得320的相同宽度。

相反,对于有效的情况,您还可以看到相同的约束链,这些约束应该将contentView.width约束为等于顶部封装布局视图的宽度。但不同之处在于,没有任何约束可以保持任何固定值为320.相反,只有左对齐约束。

所以我期望在两种情况下都看到contentView.width == parent.width,但在破案例中宽度= 320,而在工作情况下,宽度由contentView内的低优先级内部约束决定允许它扩展到大于320的值,可能是它的intrinsicContentSize。

答案 2 :(得分:1)

这不符合预期的原因是:

父视图是 UISCrollView ,水平约束设置为"H:|[contentView]|",scrollview的contentSize会将自身调整为contentView请求的宽度,而不是另一种方式。因此,autolayout引擎将首先确定contentView的维度,然后将父级的contentSize(scrollview)调整为相同的宽度。如果contentWidth比父级更窄,则不会延长,因为父级的contentSize(scrollview)将缩小到contentView的大小。

对于常规视图(非滚动视图),父视图的宽度是固定的,因此它将首先布局父视图,然后是子视图。

通过将contentView的宽度强制为与父scrollView相同的宽度,contentView将始终与父滚动视图的宽度相同,这是我想要的(和期望的)。