自动布局中的弹簧效果:在Xcode 5中使用约束使视图均匀分布

42

我了解在Interface Builder中使用旧的Struts和Springs方法来对齐、调整大小和分布视图。然而,我似乎无法弄清如何使用Xcode 5的自动布局平均分配视图。虽然在Xcode 4中有一种方法可以实现,但该选项已经消失了。

我有7个按键垂直堆叠。在3.5英寸布局上,看起来很棒。但在4英寸布局中预览屏幕时,所有按键都保持紧密排列,并且下方留有大量空间。

我希望它们保持相同的高度,但希望它们之间的空格能够灵活,以便它们可以在屏幕上展开。

输入图像描述

我已经能够使按钮的高度弯曲并填充空间,但这不是我想要的行为。我想学习如何使用自动布局来替换我的旧Springs行为,但我似乎找不到通过Interface Builder实现它的任何方法。

对于顶部的按钮,我可以接受与顶部边缘固定的空间或者与顶部边缘成比例的空间,底部按钮和底部边缘同理。这些对我来说不是很重要,我可以接受任意一种。

但我真的需要弄清楚如何平均分配视图中每个项目之间的额外空间。


1
实际上,如果你真的想要的话,你可以继续使用 Springs 和 Struts。你的故事板可以关闭自动布局。或者,如果你想同时使用自动布局和 Springs 和 Struts,可以制作一个包含按钮的父视图的 Nib (.xib 文件),将 Nib 设置为不使用自动布局,设置 Springs 和 Struts;然后,在代码中加载 Nib,取出父视图,并将其插入到你的界面中。- 然而,我已经回答了你实际的问题,即如何使用自动布局来完成它。 - matt
2个回答

84

编辑:请注意,在iOS 9中,这种技术将变得无关紧要,因为UIStackView将自动执行分布。我会添加另一个答案来解释它的工作原理。

如何使用Autolayout实现均匀分布

在Interface Builder中实现这一点的最简单方法(而不是在代码中构建约束)是使用“spacer”视图:

  1. 绝对定位上下按钮。

  2. 在所有按钮之间放置spacer视图。 使用约束将它们水平定位(水平居中最简单),并设置宽度。

  3. 使用Constant为0在每个按钮和其上下方的spacer视图之间进行约束。

  4. 现在选择所有spacer视图,并将它们的高度设置为相等。

第一张截屏展示了我在IB中设置这个:

enter image description here

我故意没有纠正“放错位置的视图”,因为我希望您在设计约束时看到它的样子。以下是4英寸和3.5英寸屏幕上的结果:

enter image description here

我让spacer视图保持黑色,以展示这种技术的工作原理,但是在实际使用中,您将使它们透明并因此不可见! 因此,用户只能看到您的按钮,均匀分布在屏幕两侧。

使用这种技术的原因是,尽管平等的概念执行了您所要求的值的分布,但约束只能在视图的方面之间应用平等性; 因此,我们需要额外的视图(spacer视图),以便我们有可以与其他物体相等的东西(这里是spacer视图的高度)。

其他方法

显然,一种更灵活的方法是在代码中分配约束条件。这听起来可能令人生畏,但有很多第三方代码可以帮助你,例如这样的工具

例如,如果我们有一个(可能是不可见的)父视图,其高度作为边界来指定四个按钮的最大垂直分布,我们可以将它们的顶部钉住到该父视图的垂直中心,使用constant值为0,但使用 multiplier 值分别为 0.000001, 0.666667, 1.33333, 和 2.0(如果有四个按钮); 现在即使父视图随屏幕高度或其他因素而改变大小,按钮也将保持垂直分布。[在Xcode 5.1中,可以在Interface Builder中设置它,但在早期版本的Xcode中不可能这样做。]


14
我对这两个选项都不满意,但那是苹果的问题,而不是你的问题。自从我们使用表格和1像素透明gif制作所有网页以来,我就没有需要使用间距了。叹气 - Kenny Wyland
我在我的自定义UITabBar/UITabBarController替代方案中概括了解决此问题的方法。只需检查我的自动布局约束生成的集成测试即可。该项目位于Github上,GGTabBar - Goles
3
嗯,同意。在受限的AutoLayout情况下是一个好的解决方案,但是天哪,感觉我们回到了用表格布局网站的90年代。UIToolbar有灵活的间隔元素,这正是所需之处,为什么不能将其作为布局元素呢? - George
3
这个解决方案使用乘数,并且不需要间隔物:https://dev59.com/KWcs5IYBdhLWcg3wLA2f#25898949 - wuf810

13
在iOS 9 / Xcode 7中,这个问题将在IB中轻松解决。只需选择按钮(或其他要垂直分布的内容)并选择编辑器 > 嵌入 > 堆栈视图。然后简单地配置堆栈视图:
  • 提供约束以定位和调整堆栈视图本身的大小。例如,将堆栈视图的四个边沿固定到其父视图的四个边沿。
  • 设置堆栈视图的属性。在这种情况下,我们希望垂直轴、填充对齐和等间距分布。
就这样!但是,你可能会好奇它是如何工作的,因为手动编码仍然可以完成相同的工作。堆栈视图通过插入空隙引导(spacer guides),而不是插入空隙视图(spacer views)来执行分配操作。引导(UILayoutGuide)是一种轻量级对象,用于布局约束,表现得像视图,但不是视图,因此不必使其无形且不承载任何视图开销。 为了说明这一点,我将在代码中实现堆栈视图的功能。假设我们有四个要垂直分布的视图。我们为它们分配了除了分配外的所有约束:
  • 它们都有绝对高度的约束
  • 它们的左侧固定在父视图的左侧,右侧固定在父视图的右侧
  • 顶部视图的顶部固定在父视图的顶部,底部视图的底部固定在父视图的底部
现在,假设我们有四个视图的引用作为一个数组:views,那么:
let guides = [UILayoutGuide(), UILayoutGuide(), UILayoutGuide()]
for guide in guides {
    self.view.addLayoutGuide(guide)
}
NSLayoutConstraint.activateConstraints([
    // guide heights are equal
    guides[1].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
    guides[2].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
    // guide widths are arbitrary, let's say 10
    guides[0].widthAnchor.constraintEqualToConstant(10),
    guides[1].widthAnchor.constraintEqualToConstant(10),
    guides[2].widthAnchor.constraintEqualToConstant(10),
    // guide left is arbitrary, let's say superview margin
    guides[0].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    guides[1].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    guides[2].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    // bottom of each view is top of following guide
    views[0].bottomAnchor.constraintEqualToAnchor(guides[0].topAnchor),
    views[1].bottomAnchor.constraintEqualToAnchor(guides[1].topAnchor),
    views[2].bottomAnchor.constraintEqualToAnchor(guides[2].topAnchor),
    // top of each view is bottom of preceding guide
    views[1].topAnchor.constraintEqualToAnchor(guides[0].bottomAnchor),
    views[2].topAnchor.constraintEqualToAnchor(guides[1].bottomAnchor),
    views[3].topAnchor.constraintEqualToAnchor(guides[2].bottomAnchor)
])

显然,我可以使用循环使代码更加简洁和精炼,但是我为了清晰起见特意展开了循环,以便您可以看到模式和技术。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接