没有显式宽度限制且尾随空间大于或等于行为不端的自动布局

Posted

技术标签:

【中文标题】没有显式宽度限制且尾随空间大于或等于行为不端的自动布局【英文标题】:Auto Layout without explicit width constraints and with greater-or-equal trailing space misbehaving 【发布时间】:2014-02-13 17:58:22 【问题描述】:

我有一个滚动视图直接包含在视图控制器的内容视图中,在两个维度上都是全尺寸。从滚动视图到其父视图(水平)和布局指南(垂直)的顶部、底部、前导和尾随空间约束都设置为 0。VC 最终意味着作为子视图控制器嵌套在一两个地方。我正在使用情节提要。

我在滚动视图中放置了一些元素并将它们限制在其中,但我看到了各种奇怪的行为。下面是一个屏幕截图,其中选择了滚动视图的所有子视图以显示它们的约束。滚动视图对***视图的四个约束在其中不可见。视图控制器已设置为 Freeform 大小,其顶层视图(以及相应的滚动视图的内容视图)高 616 pts,保证在运行时需要滚动。

在分析屏幕截图之前,这里列出了我想要实现的目标。

    元素之间的垂直间距由设计者设置并固定。 (顺便说一句,此线框中的垂直约束、文本样式等都不是最终的;整个图像仅用于说明目的。) 所有标签(除了最上面的标签)都应该从它们的固有尺寸开始,展开到滚动视图的宽度(减去两侧 20 pts 的标准 HIG 水平空间)。 按钮不太可能比这大得多,但在本地化意外的情况下,我们希望它们的行为与标签一样。 (“另一个按钮”上有一个额外的垂直 ≥ 约束;它与这个问题无关。) web view 有一个固定的高度,它的宽度应该由滚动视图的宽度决定;两侧标准 20 pt 水平空间。 文本视图具有最小高度(此处为 67 pts),但如果包含的文本太大而无法容纳,它们应该垂直扩展。它们都不可编辑或可滚动。与 Web 视图一样,它们与前缘和后缘的水平间距为标准 20 pts。

如您所见,没有一个元素有明确的宽度限制。整个事情依赖于元素和滚动视图之间的前导和尾随空间约束。在我看来,这种布局在假设的未来 320 pt 宽的 iPhone 上会有点优雅地工作,而不会改变约束。旋转到横向后它也可以工作(它可能看起来有点傻,但它会工作)。

我会一步一步讲,必要时参考截图。

1:这行得通,这里没有什么不寻常的地方。

2:标签的前导约束都是简单的 Equal 20 pt 标准空间。尾随约束是大于或等于 20 pt 标准,表面上允许它们增长到 scrollView.frame.size.width-40 宽,但不能更宽。

3:2相同。

4 和 5: 这就是有趣的地方。 Web 视图和文本视图都被列为错位视图,IB 表示它们的框架在运行时会有所不同。表示正确帧的橙色虚线边框仅水平到达具有大于或等于尾随约束的最长元素;在这里,它是“A Button With A Long Title”,它的右边缘是虚线边框边缘的结束位置。


构建这组视图及其约束,我预计会遇到一些麻烦。我知道让 UITextViews 垂直增长到高于此处定义的 ≥ 67 高度会很棘手,也许只能通过代码实现。仅通过 IB 使标签和按钮按上述规定工作似乎也有点不确定。

没有期望的是网络和文本视图报告的正确框架仅与最宽的标签或按钮一样宽。似乎通过这种设置,滚动视图实际上不会是 320 pts 宽,而只是尽可能宽以适应最长的元素及其间距,并且 web 和文本视图应该符合要求。鉴于滚动视图在所有方面都被牢牢地限制在顶层视图(设置为 320 pts 宽),我不知道为什么会这样。有些东西显然必须定义滚动视图的初始宽度,但为什么我从滚动视图到***视图的约束不这样做呢?

鉴于上述这组视图的规范,我需要更改哪些内容才能实现?

这个案例证明了我还没有真正理解 Auto Layout,我希望答案能启发我了解它的许多关键方面。

【问题讨论】:

【参考方案1】:

关于 Xcode 关于错位视图的警告,在故事板中选择视图控制器,点击画布右下角的“解决自动布局问题”按钮(它看起来像星球大战中的领带战斗机) ,然后选择“更新视图控制器中的所有帧”。这会强制您的所有视图反映它们的约束。

UIScrollView 使用自动布局是另一种动物;以至于 Apple 认为有必要发布关于该问题的技术说明:https://developer.apple.com/library/ios/technotes/tn2154/_index.html

当您连接子视图和滚动视图内壁之间的所有这些约束时,结果可能不是您所期望的。当您将子视图固定到滚动视图的两侧时,您实际上并没有确定子视图相对于滚动视图的位置。相反,您正在确定滚动视图的contentSize。这很奇怪。

您正在使用技术说明中的 Apple 所谓的纯自动布局方法。使用这种方法,子视图的约束必须指定滚动视图的contentSize

让我们只取您的一个子视图并忽略所有其余的,比如说其中一个文本视图。让我们只考虑沿水平轴的文本视图。如果您希望文本视图受滚动视图的宽度约束而不进行任何水平滚动,则需要在文本视图上安装一个固定宽度约束,该约束恰好是滚动视图边界的宽度减去间隔。完成此操作后,内容大小宽度将是左侧分隔符、文本视图宽度和右侧分隔符的总和。

很遗憾,您无法安装在文本视图宽度和滚动视图边界宽度之间建立关系的约束。这真的太糟糕了。

我实际上不建议在文本视图上安装固定宽度的约束。相反,我会重新开始并使用技术说明中的 Apple 的“混合方法”。

使用混合方法,子视图的约束不决定滚动视图的contentSize。相反,您必须显式设置滚动视图的 contentSize 和容器视图的框架(即 UIView 内容视图)。

让我们回到UITextView 和水平轴。使用混合方法,您可以保留文本视图的约束(即没有固定宽度约束)。早在viewDidLoad 就可以显式设置滚动视图contentSize 的宽度和内容视图框架的宽度。您可以将这些值显式设置为self.view.bounds.size.width,因为您的滚动视图拥抱主视图的两侧。

要实现混合方法,您必须在代码中实例化内容视图 (UIView),并且将其 translatesAutoresizingMaskIntoConstraints 属性设置为 NO。通过扩展,您可能还需要在代码中为所有这些子视图创建约束(我不知道有任何解决方法)。可视化格式化字符串乏味且重复,但在配置复杂布局(您的布局足够复杂)时,它们实际上比在 IB 中创建的约束更易于使用。

我在这里使用混合方法来解决 SO 挑战:https://***.com/a/21770179/1239263。不幸的是,该解决方案尚未经过审查。总是有可能我疯了,我不知道我在说什么。

【讨论】:

谢谢。我以前读过技术说明,但不够仔细。我最终确实采用了纯方法(在子视图和滚动视图之间使用固定宽度的容器视图),但是您的回答让我重新阅读了技术说明并提供了一些其他有用的细节,所以我已经将其标记为已接受。

以上是关于没有显式宽度限制且尾随空间大于或等于行为不端的自动布局的主要内容,如果未能解决你的问题,请参考以下文章

改变方向时UILabel没有调整

通过约束调整 NSImageView 的大小

iOS 自动布局错位视图的错误

1014. 写评语

通过示例引导列类 [重复]

闪亮的反应式下拉菜单行为不端(带有数字输入)