自动布局没有明确的宽度约束和大于或等于尾随空间的行为不当

时间:2014-02-13 17:58:23

标签: ios uiscrollview interface-builder autolayout nslayoutconstraint

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

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

Constraints visualized

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

  1. 元素之间的垂直间距由设计者设置并固定。 (顺便说一句,此线框中的垂直约束,文本样式等都不是最终的;整个图像仅用于说明目的。)
  2. 所有标签(最顶层的标签除外)应从其固有尺寸开始,向上扩展到滚动视图的宽度(减去两侧标准的HIG水平空间20磅)。
  3. 按钮不太可能比这更大,但是如果本地化意外,我们希望它们的行为就像标签一样。 (“另一个按钮”有一个额外的垂直≥约束;它与这个问题无关。)
  4. Web视图具有固定的高度,其宽度应由滚动视图的宽度决定;两侧标准20 pt水平空间。
  5. 文本视图具有最小高度(此处为67磅),但如果包含的文本太大而不适合,则它们应垂直展开。它们都不可编辑或可滚动。与Web视图一样,它们与前缘和后缘之间的水平间隔为标准的20磅。
  6. 如您所见,没有一个元素具有明确的宽度约束。整个事物依赖于元素和滚动视图之间的前导和尾随空间约束。在我看来,布局在某种程度上优雅地适用于假想的未来超过320 pt的iPhone,而不会改变约束。它也可以在旋转到横向方向后工作(它可能看起来有点傻,但它可以工作)。

    我将逐步完成这些要点,必要时参考截图。

    1:这很有效,在这里没什么特别的。

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

    3: 2 相同。

    4和5:这里有趣的地方。 Web视图和文本视图都列为Misplaced Views,IB表示它们的帧在运行时会有所不同。表示正确帧的橙色虚线边框仅水平到达具有大于或等于尾随约束的最长元素;这里,它是“带有长标题的按钮”,其右边缘是虚线边框边缘的终点。


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

    没有期望的是网页和文字视图报告的正确框架只有最宽的标签或按钮那么宽。看起来通过这种设置,滚动视图实际上不会是320点宽,而是只需要适合最长元素及其间距所需的宽度,并且Web和文本视图应该符合要求。鉴于滚动视图被严格限制在顶层视图的所有边上,设置为320点宽,我不知道为什么会这样。 SOMETHING显然必须定义滚动视图的初始宽度,但为什么不是我从滚动视图到顶层视图的约束呢?

    鉴于上述这组视图的规范,我需要做些什么来改变它?

    这个案例表明我还没有正确理解Auto Layout,我希望这些答案能让我了解它的许多重要方面。

1 个答案:

答案 0 :(得分:3)

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

使用UIScrollView自动布局是一种不同的动物;因此Apple认为有必要就此问题发布技术说明:https://developer.apple.com/library/ios/technotes/tn2154/_index.html

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

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

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

不幸的是,您无法安装一个约束来建立文本视图宽度与滚动视图边界宽度之间的关系。这真的太糟糕了。

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

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

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

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

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