1.18 配置视图

原文链接: https://developer.apple.com/documentation/swiftui/configuring-views

1.18 配置视图

通过应用视图修饰符来调整视图的特征。

概述

在 SwiftUI 中,你把视图组装成一个层级,用来描述应用的用户界面。为了帮助你自定义应用视图的外观与行为,你使用视图修饰符。例如,你可以用修饰符:

  • 为视图添加无障碍功能。
  • 调整视图的样式、布局以及其他外观特征。
  • 响应拷贝粘贴之类的事件。
  • 有条件地呈现弹出视图之类的模态视图。
  • 配置工具栏之类的辅助视图。

由于视图修饰符是由 View 协议提供行为的 Swift 方法,你可以把它们应用到任何遵循 View 协议的类型上,包括 Text、Image 和 Button 这类内置视图,以及你自己定义的视图。

用修饰符配置视图

像其他 Swift 方法一样,修饰符作用于某个实例——在这里就是某种视图——并且可以接受输入参数。例如,你可以应用 foregroundStyle(_:) 修饰符来设置 Text 视图的颜色:

1
2
Text("Hello, World!")
    .foregroundStyle(.red) // 显示红色文本。

修饰符返回一个包装原视图的视图,并在视图层级中取代原视图。你可以把上面例子中的两行理解为最终解析为一个显示红色文本的视图。

一张渲染后的文本视图截图,以前景色显示文本 Hello World。

虽然上面的代码遵循 Swift 的语法规则,但对于刚接触 SwiftUI 的开发者来说,这种代码结构可能有些陌生。SwiftUI 采用声明式方式:你在代码中与视图在视图层级中的位置相对应的位置来声明和配置视图。更多信息参见声明自定义视图。

链式使用修饰符以实现复杂效果

你通常会一个接一个地调用修饰符,形成链式调用,每个修饰符都包装前一个的结果。例如,你可以用 frame(width:height:alignment:) 修饰符把文本视图放进一个给定宽度的不可见盒子中,从而影响它的布局,然后用 border(_:width:) 修饰符在它周围画出边框:

1
2
3
Text("Title")
    .frame(width: 100)
    .border(Color.gray)

应用修饰符的顺序很重要。例如,上面代码得到的边框勾勒出整个框的宽度。

一张文本视图截图,显示字符串 Title,外面是一个比它更宽的灰色矩形边框,字符串两侧在矩形内留有空隙。图注写道:先应用 frame。

如果把 frame 修饰符放到 border 修饰符之后,SwiftUI 就只对文本视图应用边框,而文本视图占用的空间绝不会超过渲染其内容所需的大小。

1
2
3
Text("Title")
    .border(Color.gray) // 这次先应用边框。
    .frame(width: 100)

把这个视图包进固定 100 点宽的不可见盒子会影响这个复合视图的布局,但对边框没有任何影响。

一张文本视图截图,显示字符串 Title,外面紧紧包围着一个灰色矩形边框。图注写道:先应用 border。

配置子视图

你可以把 View 协议定义的任何视图修饰符应用到任何具体视图上,即使该修饰符对其目标视图没有直接影响。修饰符的效果会传播到那些没有显式覆盖该修饰符的子视图上。

例如,VStack 实例本身只负责把其他视图纵向堆叠——它没有任何要显示的文本。因此,对这个栈应用 font(_:) 修饰符对栈本身没有影响。但这个字体修饰符确实会应用到栈的任何子视图上,其中一些子视图可能会显示文本。不过,你可以通过给某个特定子视图再加一个修饰符,在局部覆盖栈上的修饰符:

1
2
3
4
5
6
7
VStack {
    Text("Title")
        .font(.title) // 覆盖该视图的字体。
    Text("First body line.")
    Text("Second body line.")
}
.font(.body) // 为栈中的文本设置默认字体。

一张由多个文本视图组成的纵向栈的截图。第一个以更大的标题字体显示 Title,其他两个以较小的正文字体分别显示 First body line 和 Second body line。

使用视图特有的修饰符

虽然许多视图类型依靠标准视图修饰符来完成自定义和控制,但有些视图确实定义了专属于该视图类型的修饰符。这类修饰符只能用在合适的视图上。例如,Text 定义了 bold() 修饰符,作为给该视图文本加粗的便捷方式。虽然因为 font(_:) 属于 View 协议而可以用在任何视图上,但 bold() 只能用在 Text 视图上。因此,你不能把它用在容器视图上:

1
2
3
4
VStack {
    Text("Hello, world!")
}
.bold() // 失败,因为 'VStack' 没有 'bold' 修饰符。

你也不能在应用了另一个通用修饰符之后再把它用在 Text 视图上,因为通用修饰符返回的是不透明类型。例如,内边距修饰符的返回值并不是 Text,而是一个无法接受粗体修饰符的不透明结果类型:

1
2
3
Text("Hello, world!")
    .padding()
    .bold() // 失败,因为 'some View' 没有 'bold' 修饰符。

正确的做法是:把粗体修饰符直接应用到 Text 视图上,然后再添加内边距:

1
2
3
Text("Hello, world!")
    .bold() // 成功。
    .padding()