Stack
Stack 可以把多个视图按照简单的规则组合为一个复合视图,按照组合的方向可以分为水平 (HStack)、垂直 (VStack)、竖直 (ZStack)。
不提供任何构造方法的参数时,Stack 默认居中对齐子视图,并在视图之间加入少量间隔。
可以使用 Stack 的 View modifier、Spacer、Divider 来更精细地定制 Stack 中的布局。
比如用三种 Stack 写一个个人信息的复合视图:
struct ProfileView: View {
var body: some View {
ZStack(alignment: .bottom) {
Image("ProfilePicture")
.resizable()
.aspectRatio(contentMode: .fit)
HStack {
VStack(alignment: .leading) {
Text("hzy1721")
.font(.headline)
Text("An ordinary person")
.font(.subheadline)
}
Spacer()
}
.padding()
.foregroundColor(.primary)
.background(Color.primary
.colorInvert()
.opacity(0.75))
}
}
}
除了默认的居中对齐,还可以在三种 Stack 的 init 方法中传入 alignment 参数,指定子元素的对齐方式。
在 HStack 中 alignment 参数的类型是 VerticalAlignment,有 .top、.center、.bottom、firstTextBaseline、.lastTextBaseline。
在 VStack 中 alignment 参数的类型是 HorizontalAlignment,有 .leading、.center、.trailing。
在 HStack 中 alignment 参数的类型是 Alignment,有 9 种取值,分别是九宫格的每个位置。
Spacer 也是 Stack 中的一种对齐方式,会尽可能多地占用主方向上的空间 (HStack 的主方向是水平,VStack 是垂直),从而使主方向上的其他视图挤压到正好能包裹内容。
比如在 HStack 中有一个 Text,默认是居中对齐的,如果在右边添加一个 Spacer,Text 就会被挤到左边,成为左对齐,在左边添加 Spacer 就是右对齐。
Divider 是一条分隔线,不会影响布局,但经常在 Stack 中使用,所以也在这里说一下。Divider 只会占用一条线的空间,不会像 Spacer 一样尽可能多地占用空间。
在前端领域中对不同屏幕尺寸的适配是一个核心的问题,要求开发者尽量使用响应式布局而不是固定布局。Stack 就是响应式布局中的一种,更容易在不同尺寸的屏幕上得到一致的效果。而 frame、position 等 View modifier 则属于固定布局。
在 View modifier 中有两个方法 background 和 overlay,可以达到与 ZStack 类似的效果,具体使用哪种取决于对最终布局的尺寸的要求。如果最终布局的尺寸应该和其中某一个视图的尺寸一致,也就是该视图的大小起决定作用,就应该使用该视图的 background 或 overlay 方法。如果最终视图的尺寸应该考虑到所有视图的大小,就可以使用 ZStack。
下面是使用 overlay 实现 ProfileView 的例子:
struct ProfileViewWithOverlay: View {
var body: some View {
VStack {
Image("ProfilePicture")
.resizable()
.aspectRatio(contentMode: .fit)
.overlay(ProfileDetail(), alignment: .bottom)
}
}
}
struct ProfileDetail: View {
var body: some View {
HStack {
VStack(alignment: .leading) {
Text("Rachael Chiseck")
.font(.headline)
Text("Chief Executive Officer")
.font(.subheadline)
}
Spacer()
}
.padding()
.foregroundColor(.primary)
.background(Color.primary
.colorInvert()
.opacity(0.75))
}
}
这里的效果与 ZStack 一致。
三种 Stack 都遵循 View 协议,可以使用各种 View modifier,下面介绍一下每个 Stack 的 init 方法。
HStack
init(alignment: VerticalAlignment = .center, spacing: CGFloat? = nil, content: () -> Content)
-
VerticalAlignment 有 .top、.center、.bottom、firstTextBaseline、.lastTextBaseline。
-
spacing 是子视图之间的间隔,设为 nil 使用默认大小的间隔。
-
content 就是子视图,Content 是一个泛型类型,可以根据实际的子视图内容推断出来。这个参数实际上使用了 @ViewBuilder 注解,可以使用闭包构造视图。
VStack
init(alignment: HorizontalAlignment = .center, spacing: CGFloat? = nil, content: () -> Content)
- HorizontalAlignment 有 .leading、.center、.trailing。
ZStack
init(alignment: Alignment = .center, content: () -> Content)
- Alignment 有 9 种取值,上面讲过。