使用 UIStackView 来简化 AutoLayout 布局
可以维护的 AutoLayout
在使用 AutoLayout
做界面布局的时候,你会发现,做好一个复杂界面的约束并不是一件容易的事,经常需要很多的调试,这在用代码写布局的时候尤为明显,因为没有 IB 给你实时的反馈当前的 Layout 错误。而且不管是在 IB 还是纯代码的 AutoLayout
,其可读性都不太好,例如我们创建了下面这样一个 App Store 的界面:
Label
和 星标图片之间有很多约束,如果我们遇到了需求改动,想要在星标上方多加一行,就得先搞清楚各个约束是干什么的,再打破几个约束,最后增加一个 Label
并且加上约束,费时费力。
iOS 9 发布的时候苹果提供了一个新的AutoLayout
工具 UIStackView
,可以自动完成很多类型的布局,并且不用添加任何约束,由 UIStackView
自行管理其 ArrangedSubviews
的约束并且可以让增删变的更简单,大大增加了项目的可维护性,本文就来介绍一下 UIStackView
的使用。
UIStackView
本身是建立在 AutoLayout
的约束之上的,我们不用主动给其子视图添加约束就可以完成自动布局,UIStackView
相当于一个可以为你自动管理内部 View 的一个容器。想要完成布局大多数情况你需要做这三步:
1.调用 -addArrangedSubview: 添加需要布局的 View 到一个 UIStackView 上;
2.配置 UIStackView 的 axix、alignment、distribution、spaciing 这四个属性;
3.如果有必要,再嵌套多个 UIStackView 到一个 UIStackView 中。
UIStackView 的主要方法和属性
2.png@property(nonatomic) UILayoutConstraintAxis axis;
用于控制 UIStackView
子视图横向或纵向布局,有两种:
-
UILayoutConstraintAxisHorizontal
横向布局 -
UILayoutConstraintAxisVertical
纵向布局
@property(nonatomic) UIStackViewAlignment alignment;
这个属性稍微复杂一点,当 axis
是 Horizontal
时其表示 UIStackView
的子视图相对于其上下边界的对其方式。当 axis 为 Vertical 时,表示 UIStackView 的子视图相对于其左右边界的对其方式,一共有八中,看下面的图示就很好理解了:
@property(nonatomic) UIStackViewDistribution distribution;
这个属性用来控制 UIStackView
在其 axis
方向上的分布,分别有如下几种:
Fill
UIStackView
的子视图会沿着其 axis
填充满 UIStackView
,如果子视图在 axis
方向上不能放入 StackView
(StackView
太小),则会按照子视图的抗压缩优先级属性( Content Compression Resistance Priority)从低到高压缩视图,直到正好填满 StackView
为止。
反之,如果子视图在 axis
方向不能填满 StackView
(StackView
太大),则会按照子视图的抗拉伸优先级属性(Content Hugging Priority)从低到高拉伸视图,知道正好填满 StackView 为止。
这里说一点,StackView
在没有宽高约束的时候大部分情况是没问题的,因为它会根据子视图的intrinsic content size
、宽高约束、甚至是 frame.size.width
frame.size.height
来计算自己的宽高,最终会有一个intrinsic content size
,这样我们把 StackView 添加到父视图中的时候,可以不约束其宽高,只定位好 origin
就可以了。
FillEqually
这个属性简单一些,StackView
的子视图会沿着axis
在 StackView
内均匀分布。
FillProportionally
按字面意思解释,和 FillEqually
类似,也是每个子视图都会被等比例拉伸或者压缩,来填满 StackView
,但是这个是等比例填满,每个子视图会被 StackView
按照其 intrinsic content size
或者其本身 frame
来进行等比例缩放,如果要拉伸或者压缩,每个子视图都会同时被等比例拉伸或压缩。
EqualSpacing
StackView
会在每个子视图中间留下等相等的空间距,此时 Spacing
属性定义的是这个间距的最小值。如果 StackView
足够大,可以保持子视图的原始尺寸,不拉伸,但是如果 StackView 太小
,子视图会被压缩,也是按照子视图的抗压缩优先级属性( Content Compression Resistance Priority)从低到高压缩,此时如果有两个子视图有相等的抗压缩优先级,StackView
就不知道压缩哪个了,需要调整子视图的优先级,不要出现相等的情况。
EqualCentering
StackView
会把每个子视图按照中心点等距离排列,此时 Spacing
属性定义的是这个间距的最小值。如果 StackView
足够大,可以保持子视图的原始尺寸,不拉伸,但是如果 StackView
太小,子视图会被压缩,也是按照子视图的抗压缩优先级属性( Content Compression Resistance Priority)从低到高压缩,此时如果有两个子视图有相等的抗压缩优先级,StackView
就不知道压缩哪个了,需要调整子视图的优先级,不要出现相等的情况。
@property(nonatomic) UIStackViewAlignment alignment;
按照 distribution
属性中的布局方式控制子视图之间的间距。
总结
在本文结束之前,我们看一下上面的 AppStore Demo 在使用 UIStackView 布局后的情况,是不是简介清爽了许多。
7.png最后,引用一下 WWDC 苹果工程师的话,如果你使用 AutoLayout,那么请 Start With Stack View, use constraints as needed!