文章

使用 Stack 视图构建布局

通过原生容器视图构建复杂布局。

概览

单独的 HStack (英文)VStack (英文)ZStack (英文) 均属于简单视图。HStack (英文) 可以将视图置于水平线上,VStack (英文) 可以将视图置于垂直线上,而 ZStack (英文) 则可以将视图相互叠放。

示意图显示了三种不同的 stack 视图:水平、垂直和深度。每种 stack 都包含一个正方形和一个圆形,它们水平或垂直并排堆叠在一起,或相互叠放。

当你使用默认参数初始化它们时,stack 视图会将其内容居中对齐,并在每个所含视图之间插入少量间隔。但是,当你使用视图修饰符 Spacer (英文)Divider (英文) 视图合并和自定 stack 时,你可以创建高度灵活的复杂布局。

规划你的布局层次结构

当你开始将设计转换为代码时,在布局方面,请考虑可以如何使用各种类型的 stack 视图创建布局。将复杂设计细分为你可以通过 stack 视图构建的更小、更简单的部分。

例如,你可以使用三种 stack 视图构建以下个人资料视图:

示意图显示了普通用户个人资料布局如何利用 stack 视图。示意图显示了已渲染的布局,旁边是视图层次结构的三维分解图,其中展示了四层视图相互叠放。层次结构中的最低一层是 ZStack;在此之上是 Image 视图,然后是 HStack,最后是位于最高一层的 VStack 和 Spacer 视图。

ZStack (英文) 包含一个显示个人资料图片的 Image (英文) 视图,后者顶部叠加了一个半透明 HStack (英文)HStack (英文) 包含一个 VStack (英文),后者内部有一对 Text (英文) 视图,Spacer (英文) 视图将 VStack (英文) 推向前缘侧。

若要创建这个 stack 视图,请使用以下代码:


struct ProfileView: View {
    var body: some View {
        ZStack(alignment: .bottom) {
            Image("ProfilePicture")
                .resizable()
                .aspectRatio(contentMode: .fit)
            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))
        }
    }
}

通过 Alignment 和 Spacer 视图确定视图位置

通过组合使用 alignment 属性、Spacer (英文)Divider (英文) 视图,对齐 stack 视图中包含的任意视图。

在前面的示例布局中,包含两个 Text (英文) 视图的 VStack (英文) 使用 leading (英文) 对齐:

示意图显示了两个视图在垂直 stack 视图中以三种不同方式对齐。左对齐,表示两个视图都与 stack 的前缘对齐。居中对齐,表示两个视图都在 stack 中居中对齐。最后是右对齐,表示两个视图都与后缘对齐

alignment 属性不会调整 VStack (英文) 相对于其父容器的布局,而是调整 VStack (英文) 内部视图的布局。

VStack (英文)alignment 属性仅会应用于使用 HorizontalAlignment (英文) 对所含控件的水平对齐。同样,HStack (英文)alignment 属性仅控制使用 VerticalAlignment (英文) 的垂直对齐。最后,你可以使用 Alignment (英文) 沿两个轴对齐 ZStack (英文) 中的视图。

Spacer (英文) 视图用于沿 HStack (英文)VStack (英文) 的主轴对齐视图。Spacer 会进行扩展以填充所有可用空间,并将内容从其他视图或 stack 的边缘推开。

示意图显示了三个垂直 stack 视图中的 spacer 视图使用情况。第一个 stack 显示 spacer 视图将另一个视图推到其容器的顶部。第二个 stack 显示 spacer 视图将两个视图推开,使得这两个视图分别与其容器的顶部和底部对齐。spacer 视图将另一个视图推到其容器的底部。

Divider (英文) 视图还会在 stack 的子视图之间添加空间,但仅会插入足够沿 stack 的副轴绘制一条直线的空间。它们不会进行扩展来填充可用的空间。

创建自适应布局而不是显式布局

尽可能定义结构和层次结构,而不是显式确定视图框架的位置。不要为视图使用显式高度和宽度,而应让它们进行扩展以填充可用空间。你构建的自适应布局更容易适应不同的设备大小和平台。

通过以显式方式操控 Text (英文) 视图框架,可以使用两个而不是三个 stack 视图来创建这篇文章的示例布局。尽管输出看起来可能相同,但用来实施的代码更加脆弱,并且还可能无法在不同大小类别的设备之间进行缩放。

你可能需要使用 frame(width:height:alignment:) (英文)position(x:y:) (英文) 等视图修饰符来对使用显式调整的布局进行调整,但仅当你无法以灵活的自适应方式获得所需布局才应考虑此方法。有关对视图布局进行精细调整的更多信息,请参阅“对视图的位置进行精细调整 (英文)”。

以替代方式添加深度

在某些情况下,可能需要使用 overlay(_:alignment:) (英文)background(_:alignment:) (英文) 视图修饰符而不是 ZStack (英文) 来为你的布局添加深度。Background 视图修饰符将另一个视图置于你正在修饰的视图之后,overlay 则是在其顶部放置一个视图。

根据你想要如何确定最终布局的大小,在基于 stack 的方法和视图修饰符方法之间进行选择。如果你的布局有一个定义布局大小的主导视图,请在该视图上使用 overlay(_:alignment:) (英文)background(_:alignment:) (英文) 视图修饰符。如果你想通过汇总所有所含视图得出最终视图大小,请使用 ZStack (英文)

例如,以下代码会在 Image (英文) 视图的顶部叠加一个 ProfileDetail 视图:


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))
    }
}

另请参阅

Stack