SwiftUI 布局: Alignment & AlignmentGuides

SwiftUI 为我们提供了许多有价值的方法来控制视图的对齐方式,我将逐一介绍这些方法,以便您可以看到它们的实际操作。

  • 最简单的对齐选项是使用frame()修饰符的alignment参数。

请记住,文本视图总是使用显示其文本所需的精确宽度和高度,但是当我们在其周围放置边框时,它可以是任何大小。由于父对象对子对象的最终大小没有发言权,因此这样的代码将创建一个300x300的框架,并在其内部以较小的文本视图为中心:

Text("Live long and prosper")
    .frame(width: 300, height: 300)
SwiftUI 布局: Alignment & AlignmentGuides_第1张图片

如果不希望文本居中,请使用frame()alignment参数。例如,当在从左到右的环境中运行时,此代码将视图放在左上角

    .frame(width: 300, height: 300, alignment: .topLeading)
SwiftUI 布局: Alignment & AlignmentGuides_第2张图片

然后可以使用offset(x:y:)在 frame 内移动文本。

  • 下一个选项是使用堆栈的对齐参数。

例如,这里有四个大小不同的文本视图排列在HStack中:

HStack {
    Text("Live")
        .font(.caption)
    Text("long")
    Text("and")
        .font(.title)
    Text("prosper")
        .font(.largeTitle)
}
SwiftUI 布局: Alignment & AlignmentGuides_第3张图片

我们没有指定对齐方式,因此默认情况下它们将居中。这看起来不太好,所以您可能会考虑将它们全部对齐到一个边,以获得更整洁的线条,如下所示:

HStack(alignment: .bottom) {
SwiftUI 布局: Alignment & AlignmentGuides_第4张图片

然而,这看起来也很糟糕:因为每个文本视图的大小不同,它们也有不同的基线——这就是“abcde”之类的字母在一行上的名称,这不包括行下方的字母,如“gjpy”。因此,小文本的底部低于较大文本的底部。

幸运的是,SwiftUI有两个特殊的对齐方式,可以在第一个子级或最后一个子级的基线上对齐文本。这将导致堆栈中的所有视图在一个统一基线上对齐,而不管它们的字体是什么:

HStack(alignment: .lastTextBaseline) {
SwiftUI 布局: Alignment & AlignmentGuides_第5张图片

接下来,为了实现更细粒度的控制,我们可以为每个单独的视图定制“对齐”的含义。为了更好地了解这是如何工作的,我们将从以下代码开始:

struct ContentView: View {
    var body: some View {
        VStack(alignment: .leading) {
            Text("Hello, world!")
            Text("This is a longer line of text")
        }
        .background(Color.red)
        .frame(width: 400, height: 400)
        .background(Color.blue)
    }
}

运行时,您将看到VStack紧紧围绕着两个文本视图,背景为红色。两个文本视图的长度不同,但由于我们使用了.leading对齐方式,因此在从左到右的环境中,它们都将与它们的左边缘对齐。除此之外还有一个蓝色背景的大框架。因为frame大于VStack,所以以VStack中间为中心。

SwiftUI 布局: Alignment & AlignmentGuides_第6张图片

现在,当VStack开始对齐这些文本视图时,它要求它们提供它们的 leading。默认情况下,这很明显:它使用视图的左边缘或右边缘,具体取决于系统语言。但是如果我们想改变这一点呢?如果我们想让一个视图具有自定义对齐方式呢?

SwiftUI为此提供了alignmentGuide()修饰符。这需要两个参数:我们要更改的指南和返回新对齐方式的闭包。闭包被赋予一个ViewDimensions对象,该对象包含其视图的宽度和高度,以及读取其各种边的能力。

默认情况下,视图的.leading对齐向导是其.alignmentGuide(.leading)——我知道这听起来很明显,但实际上它相当于:

VStack(alignment: .leading) {
    Text("Hello, world!")
        .alignmentGuide(.leading) { d in d[.leading] }
    Text("This is a longer line of text")
}

我们可以重写该对齐向导,以使用视图的 trailing 作为其leading gaide 对齐向导,如下所示:

VStack(alignment: .leading) {
    Text("Hello, world!")
        .alignmentGuide(.leading) { d in d[.trailing] }
    Text("This is a longer line of text")
}

SwiftUI 布局: Alignment & AlignmentGuides_第7张图片

现在您将看到我为什么要添加颜色:第一个文本视图将向左移动,以便它的右边缘位于下面视图的左边缘的正上方,VStack将展开以包含它,整个内容仍将位于蓝色框架的中心。

此结果与使用offset()修饰符不同:如果偏移文本,则其原始尺寸实际上不会更改,即使生成的视图在不同的位置呈现。如果我们偏移了第一个文本视图而不是改变它的对齐指南,VStack不会扩展到包含它。

虽然对齐导向闭包已传递给视图的尺寸,但如果不想使用,则不需要使用它们–可以发回硬编码的编号,或创建其他计算。例如,通过将10个文本视图的位置乘以-10,可以为10个文本视图创建一个分层效果:

var body: some View {
    VStack(alignment: .leading) {
        ForEach(0..<10) { position in
            Text("Number \(position)")
                .alignmentGuide(.leading) { _ in CGFloat(position) * -10 }
        }
    }
    .background(Color.red)
    .frame(width: 400, height: 400)
    .background(Color.blue)
}
SwiftUI 布局: Alignment & AlignmentGuides_第8张图片

要完全控制对齐指南,您需要创建自定义对齐指南。我认为这本书应该有一个小章节…

Alignment and alignment guides

你可能感兴趣的:(SwiftUI 布局: Alignment & AlignmentGuides)