在上一节中,看到当没有加入任何限制时,HStack是如何分配空间给自己的子元素的,通过迭代依次进行。在本节中,将讲解.layoutPriority
和.frame
这两个modifier是如何影响布局结果的。
布局优先级修饰符.layoutPriority
继续使用上一节中的例子,使用HStack依次平铺三个Text,下面的示例代码中,没有任何的优先级区别,采用默认的方式进行布局。
struct DemoView: View {
var body: some View {
HStack {
Text("A great and warm welcome to Kuchi")
.background(Color.red)
Text("A great and warm welcome to Kuchi")
.background(Color.red)
Text("A great and warm welcome to Kuchi")
.background(Color.red)
}.background(Color.yellow)
}
}
那么.layoutPriority
这个modifier是什么呢?先来看一下官方文档的解释:
Sets the priority by which a parent layout should apportion space to the child.
The default priority is 0. In a group of sibling views, raising a view’s layout priority encourages that view to shrink later when the group is shrunk and stretch sooner when the group is stretched.
A parent layout should offer children with the highest layout priority all the space offered to the parent minus the minimum space required for all its lower-priority children, and so on for each lower priority value.
提炼一下重点:
- 当没有使用该modifier时,默认值为0;
- 回想一下上一节中的布局迭代的顺序,父视图在挑选子元素布局的顺序,依赖于该modifier的大小,modifier值越大,优先级越高;
- 取得最高优先级的元素后,该子元素可用于布局的空间是父视图空间减去剩下元素个数乘以一个
minimum space
,再减去子元素间的默认间隔(这个默认间隔可以通过Stack的初始化函数进行修改)后的大小,这个minimum space
在之后的实例中可以看到。
修改一下之前的示例代码,添加.layoutPriority
struct DemoView: View {
var body: some View {
HStack {
Text("A great and warm welcome to Kuchi")
.layoutPriority(-1)
.background(Color.red)
Text("A great and warm welcome to Kuchi")
.layoutPriority(1)
.background(Color.red)
Text("A great and warm welcome to Kuchi")
.background(Color.red)
}.background(Color.yellow)
}
}
它们的优先级分别为-1,1,0。最后一个Text由于没有给它设置优先级,所以它的优先级为默认值0,得到的结果如下图所示:

从这个结果中,可以看到第一个Text由于优先级最低-1,被压缩成了一个细条,这个细条的宽度就是上面解释中所说的
minimum space
,两个Text中间的间隔就是一个默认间隔。
小结
当设置了优先级以后,布局的过程便和上一节中所说的有所不同了,总结如下:
- 父视图给出了可用于布局(可以自己支配)的frame;
- 当有多个子视图时,在子视图中进行挑选找出优先级最高的,选择第一个子视图。
- 对第二步找到的子视图进行布局,把自己的可支配的大小减去剩下元素个数乘以
minimum space
,再减去子元素间的间隔,将这一部分空间分配给该视图进行布局- 子视图基于父视图提供的frame来看看自己到底占据的大小
- 子视图算好之后,父视图会减去这个子视图所占据的size,把剩下的部分分配给其余的子视图,回到步骤2,进行下一次循环,直到所有子视图绘制完成。
- 当所有子视图绘制结束后,父视图再根据子视图所占据空间来调整自己的size
当有多个子视图具有相同优先级时,第三步的计算方式将优先不同:
对第二步找到的子视图进行布局,把自己的可支配的大小减去剩下低优先级元素个数乘以
minimum space
,再减去子元素间的间隔,将这一部分空间均分给和当前视图具有相同优先级的元素,而这个元素所占面积为其中一份。

Tips: 如果对于这种布局结果有疑问,比如为何第一个和第二个Text所占的空间为什么会不相同,请查阅上一节。
frame修饰符
那么再来看看frame修饰符,frame具有最高的优先级,布局时,会优先为有frame的子视图进行布局,示例代码如下:
struct DemoView: View {
var body: some View {
HStack {
Text("A great and warm welcome to Kuchi")
.frame(width: 200, height: 200)
.background(Color.red)
Text("A great and warm welcome to Kuchi")
.layoutPriority(1)
.background(Color.red)
Text("A great and warm welcome to Kuchi")
.background(Color.red)
}.background(Color.yellow)
}
}

frame是最好理解的布局修饰符,尤其对应熟悉UIKit的同学,在这里也就无需多介绍了。了解了视图的布局后,其实会发现,在平时的开发过程中,他们的使用场景其实并不多,通常情况下,都会给出固定的位置,来放置元素。只有当我们需要类似自动填充的流式布局时,才需要了解并掌握这些布局的原理。
题外话
在之前的讲解中说到了布局中,两个相邻子元素的默认间隔,这个间隔可以通过构造函数进行修改:
@inlinable public init(alignment: VerticalAlignment = .center, spacing: CGFloat? = nil, @ViewBuilder content: () -> Content)
它的第二个参数spacing就是间隔,默认值为nil,也就是默认间隔。可以通过将其设置为0,来取消这个间隔。
网友评论