2.5 跨栈对齐视图

原文链接: https://developer.apple.com/documentation/swiftui/aligning-views-across-stacks

2.5 跨栈对齐视图

创建自定义对齐方式,并用它在多个栈之间对齐视图。

概述

当你把多个栈嵌套在一起时,可能希望这些栈中的某些特定项目彼此对齐。默认情况下,你为某个栈指定的对齐方式只作用于该栈的子视图。要让位于嵌套栈中的子视图彼此对齐,请定义一种自定义对齐方式,把它指定给外层视图,并使用 alignment guide 修饰符来标识需要对齐的特定视图。

从默认的居中对齐开始

为了说明如何跨栈对齐项目,下面这个视图展示了一个水平栈,它包裹着两个子视图数量不同的嵌套垂直栈。外层的 HStack 没有定义对齐方式,因此默认使用 center。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct ImageRow: View {
    var body: some View {
        HStack {
            VStack {
                Image("bell_peppers")
                    .resizable()
                    .scaledToFit()
                Text("Bell Peppers")
                    .font(.title)
            }
            VStack {
                Image("chili_peppers")
                    .resizable()
                    .scaledToFit()
                Text("Chili Peppers")
                    .font(.title)
                Text("Higher levels of capsicum")
                    .font(.caption)
            }
        }
    }
}

下图显示嵌套垂直栈中的内容在栈的中央对齐。这些垂直栈内部的子元素(例如每个图像下方的标题)彼此并不对齐。

两个栈,各包含一个图像和图像下方的标题,垂直居中。左侧栈包含一个纵向的图像,其下方是标题 Bell Peppers。右侧栈包含一个横向的图像,其下方是标题 Chili Peppers,另外还有一条说明文字 Higher levels of capsicum。

定义自定义对齐方式

要创建新的垂直对齐参考线,请为 VerticalAlignment 扩展一个新的静态属性作为你的参考线。参考线的命名应与它对齐的对象相关,以便于使用。下面的示例把这个参考线的默认值设为底部定位:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
extension VerticalAlignment {
    /// 用于图像标题的自定义对齐方式。
    private struct ImageTitleAlignment: AlignmentID {
        static func defaultValue(in context: ViewDimensions) -> CGFloat {
            // 如果没有设置参考线,默认采用底部对齐。
            context[VerticalAlignment.bottom]
        }
    }

    /// 用于对齐标题的参考线。
    static let imageTitleAlignmentGuide = VerticalAlignment(
        ImageTitleAlignment.self
    )
}

指定并应用自定义对齐方式

要使用这条对齐参考线,请把它指定给包围着你想对齐的那些视图的父视图。下面的示例把 imageTitleAlignmentGuide 指定为这个水平栈的对齐方式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
struct RowOfAlignedImages: View {
    var body: some View {
        HStack(alignment: .imageTitleAlignmentGuide) {
            VStack {
                Image("bell_peppers")
                    .resizable()
                    .scaledToFit()

                Text("Bell Peppers")
                    .font(.title)
            }
            VStack {
                Image("chili_peppers")
                    .resizable()
                    .scaledToFit()

                Text("Chili Peppers")
                    .font(.title)

                Text("Higher levels of capsicum")
                    .font(.caption)
            }
        }
    }
}

这两个垂直栈现在按栈的底部对齐,使用的是你为自定义参考线指定的默认值。

两个栈,各包含一个图像和图像下方的标题,按栈的底部对齐。左侧栈包含一个纵向的图像,其下方是标题 Bell Peppers。右侧栈包含一个横向的图像,其下方是标题 Chili Peppers,另外还有一条说明文字 Higher levels of capsicum。

当你在某个栈上定义对齐方式后,它会透传到所包含的子视图中。在嵌套的 VStack 实例内部,对需要对齐的视图应用 alignmentGuide(_:computeValue:),并使用你为 HStack 定义的自定义参考线。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct RowOfAlignedImages: View {
    var body: some View {
        HStack(alignment: .imageTitleAlignmentGuide) {
            VStack {
                Image("bell_peppers")
                    .resizable()
                    .scaledToFit()

                Text("Bell Peppers")
                    .font(.title)
                    .alignmentGuide(.imageTitleAlignmentGuide) { context in
                        context[.firstTextBaseline]
                    }
            }
            VStack {
                Image("chili_peppers")
                    .resizable()
                    .scaledToFit()

                Text("Chili Peppers")
                    .font(.title)
                    .alignmentGuide(.imageTitleAlignmentGuide) { context in
                        context[.firstTextBaseline]
                    }

                Text("Higher levels of capsicum")
                    .font(.caption)
            }
        }
    }
}

alignment guide 修饰符的闭包返回 firstTextBaseline,它把各个标题的基线对齐到 imageTitleAlignmentGuide 这条对齐参考线上。

两个栈,各包含一个图像和图像下方的标题,按标题对齐。左侧栈包含一个纵向的图像,其下方是标题 Bell Peppers。右侧栈包含一个横向的图像,其下方是标题 Chili Peppers,另外还有一条说明文字 Higher levels of capsicum。