StackView

1. 伸缩优先级

1.1 基本概念

1.1.1 固有内容尺寸(intrinsic content size)

在开发中,有的控件或视图其本身就自带大小。这些控件能通过自己显示的内容计算出需要的 Size,这个自动计算出来 Size 就叫该控件的固有内容尺寸(intrinsic content size)。 比如 UIButton、UILabel 控件,我们设置完显示文字后,它就能根据字符串长度自动计算出宽高尺寸。同样情况的还有 UIImageView。

1.1.2 约束优先级

  • 在没有 AutoLayout 的时候,我们通过 init(frame: CGRect) 方式来指定 UIView 的位置和大小。
  • 而使用 AutoLayout 的时候,则通过约束来确定 UIView 的位置和大小。
  • 为了解决多个约束冲突的问题,在 Autolayout 中每个约束都有一个优先级, 优先级的范围是 1 ~ 1000。创建一个约束,其默认的优先级是最高的 1000。
  • 有了优先级,系统就可以通过比较两个“相互冲突的约束”的优先级,从而忽略低优先级的某个约束,达到正确布局的目的。

1.1.3 伸缩优先级

(1)对于像 UILabel、UIButton、UIImage 等具有固有内容尺寸的控件,我们可以只为其指定位置,它们会自动根据 intrinsic content size 显示相应的大小。

(2)通常情况下,这种不指定尺寸的布局不会出现问题。但在某些情况下,比如两个 UILabel 布局互相影响的话,就会造成组件间的 Intrinsic 冲突。

(3)要解决冲突,就要用到 UIView 自带的模糊约束:content Hugging 和 content Compression Resistance 了。我们需要做的就是设置这两个约束的优先级,它们具体功能如下:

  • Content Hugging Priority:表示一个控件“抗拉伸”的优先级。优先级越高,越不容易被拉伸,默认是:251。
  • Content Compression Resistance Priority:表示一个控件“抗压缩”的优先级。优先级越高,越不容易被压缩,默认是:750。

1.2 Content Hugging Priority / Content Compression Resistance Priority 使用

1.2.1 Intrinsic 冲突样例

在进行操作之前,我们需要将storyboard中,进行下面的设定。 双击Main.storyboard,在右侧文件观察器中, "Use Safe Area Layout Guides" 选项取消选中。

经过上记的设置之后,开始下记的操作:

(1) 假设我们需要实现如下样式,左侧显示文章标题,右侧显示日期。

(2) AutoLayout布局的话,首先在单元格内部拖如两个 UILabel,并设置好相关样式属性。

(3) 然后我们对标题Label设置上下左右4个约束。

(4) 而对于日期Label则设置上下右3个约束。

(5) 这两个标签我们都没设置尺寸约束,想让它们根据内容自己设置大小。但上面的约束添加完会发现系统提示有约束冲突错误:

(6) 而程序运行后的效果如下,与我们想要的效果不一样。

1.2.2 问题原因

由于默认情况下,两个 UILabel 的抗拉伸、抗压缩优先级都是一样的。当单元格的空间不足以完全显示出标题和日期内容时,系统就不知道该压缩哪个 Label 的宽度。又或者空间充裕时,系统不知道该拉伸哪个 Label 的宽度。

1.2.3 解决办法

(1) 我们希望在空间不足的情况下,优先压缩标题Label的宽度,那么可以将其抗压缩优先级调低,改成:749。 (2) 而在空间充足的情况下,同样优先拉伸标题Label的宽度,那么同样将其抗拉伸优先级调低,改成:250。

(3) 再次运行程序,便发现内容显示正常了。

2. UIStackView使用

2.1 基本介绍

2.1.1 什么是UIStackView?

  • UIStackView 是 UIView 的子类,它不能用来呈现自身的内容,而是用来约束子控件的一个控件。
  • UIStackView 提供了一个高效的接口用于平铺一行或一列的视图组合。对于嵌入到 StackView 的视图,我们不用再添加自动布局的约束了。Stack View 会管理这些子视图的布局,并帮我们自动布局约束。也就是说,这些子视图能够适应不同的屏幕尺寸。
  • UIStackView 支持嵌套,我们可以将一个 Stack View 嵌套到另一个 Stack View 中,从而实现更为复杂的用户界面。
  • 使用 UIStackView 并不意味这不需要处理自动布局了。我们仍旧要定义一些布局约束来约束 Stack View。它只是帮我们节约了为每个 UI 元素创建约束的时间,同时它更容易的从布局中添加/删除一个视图。

2.1.2 如何使用UIStackView?

我们可以直接在代码中使用 UIStackView,也可以在 StoryBoard 中使用。 Xcode 提供了两种方式在 StoryBoard 中使用 UIStackView:

  • 从对象库中拖一个 Stack View(水平的/竖直的)放到 storyboard 的正确位置上。然后就可以拖一些 label、button、imageView 等视图放到 Stack View 中。
  • 可以在自动布局栏中使用 Stack 选项。比如我们简单的选择两个或更多的视图,之后点击 Stack 选项,IB 将会把这些视图嵌入到一个 Stack View 中,并自动的调整。

2.1.3 UIStackView的属性

UIStackView 使用简单,属性只有如下 4 个:

(1) Axls:子视图的布局方向,可选值有:

  • Vertical :垂直
  • Horizontal:水平

(2) Alignment:子视图的对齐方式,可选值有:

  • Fill :子视图填充 StackView。
  • Leading :靠左对齐。
  • Trailing :靠右对齐。
  • Center :子视图以中线为基准对齐。
  • Top :靠顶部对齐。
  • Bottom :靠底部对齐。
  • First Baseline:按照第一个子视图中文字的第一行对齐。
  • Last Baseline :按照最后一个子视图中文字的最后一行对齐。

(3) Distribution:子视图的分布比例(大小),可选值有:

  • Fill :默认分布方式。
  • Fill Equally :每个子视图的高度或宽度保持一致。
  • Fill Proportionally:StackView 自己计算出它认为合适的分布方式。
  • Equal Spacing :每个子视图保持同等间隔的分布方式。
  • Equal Centering :每个子视图中心线之间保持一致的分布方式。

(4) Spacing:子试视图间的间距

2.2 上中下三段式布局

(1) 首先向StoryBoard添加三个按钮,并设置后相应样式

(2) 选中三个按钮,为其添加StackView。这时候,我们会看有冲突,这是因为没有为各个子View添加约束

(3) 为StackView设置约束。这时候,我们还是发现有冲突,这是因为子View的优先级相同,StackView不知道为谁压缩或拉伸相应的宽度或高度。

(4) 我们可以选中右侧窗口中Distributlon,为其子View设定相应的比例宽度和高度

(5) 这时候,我们发现布局已经完成了

2.3 重写布局

当我们从竖屏旋转到横屏时,如果我们想要实现在竖屏时子控件垂直布局,那么我们可以通过 Size Classes 重写原来的基本布局。

(1) 点击 StackView 的 Axis 选项旁边的 + 按钮。Width 选择 Any,Height 选择 Compact。然后点击“Add Variation”添加

(2) 最后将这个新增的 Size Classes 下的 Axis 值设置为 Horizontal。

(3) 运行程序即可看到效果,同时横竖屏切换时布局方向也会自动改变。

3. 等比例布局<以垂直布局为例,横布局类似>

(1)拖动一个StackView到storyboard中,并为StackView添加相应的约束,将StackView的高,宽度设置与父布局高度一致
(2)添加三个按钮到StackView中,设置相应的样式之后,选中三个button添加等高约束
(3)选择其中一个button右侧的等到约束线,在右侧属性观察器中,设置属性,进行相应比例的设定
(4)运行程序或者点击预览按钮进行布局查看
模拟器运行的结果:

查看预览步骤:
第一步:

第二步:

第三步: