每当我们将修饰符应用于 SwiftUI
视图时,我们实际上都会创建一个,应用了更改的新视图 —— 我们不仅仅是修改现有的视图。 如果你仔细想想,这种行为是有道理的 —— 我们的视图仅保留我们赋予它们的确切属性,因此,如果我们设置背景颜色或字体大小,则无处存储该数据。
我们将在下一章中查看为什么会发生这种情况,但是首先,我想看看这种行为的实际含义。看一下这段代码:
代码语言:javascript复制Button("Hello World") {
// do nothing
}
.background(Color.red)
.frame(width: 200, height: 200)
您认为它运行时会是什么样?
您很可能猜错了:您不会在中间看到带有 “Hello World”
的 200x200
红色按钮。相反,您会看到一个 200x200
的空正方形,中间是 “Hello World”
,在 “Hello World”
周围有一个红色矩形。
如果思考一下修饰符的工作原理,您就可以了解为什么会如此:每个修饰符都会创建一个,应用了该修饰符的新结构体,而不是在视图上设置属性。
您可以通过查询视图主体的类型来窥视 SwiftUI
的底层。将按钮修改为如下:
Button("Hello World") {
print(type(of: self.body))
}
.background(Color.red)
.frame(width: 200, height: 200)
Swift 的 type(of:)
方法会打印特定值的确切类型,在这种情况下,它将打印以下内容:ModifiedContent<ModifiedContent<Button<Text>, _BackgroundModifier<Color>>, _FrameLayout>
您可以在这里看到两件事:
- 每次我们修改视图时,SwiftUI 都会使用以下泛型来应用该修饰符:
ModifiedContent<OurThing, OurModifier>
- 当我们应用多个修饰符时,它们会叠加在一起:
ModifiedContent<ModifiedContent<…
要了解该类型是什么,请从最里面的类型开始,然后逐步解决:
- 最里面的类型是
ModifiedContent<Button<Text>, _BackgroundModifier<Color>
:您的按钮上有一些带有背景色的文本。 - 在外部,我们有了
ModifiedContent<…, _FrameLayout>
,它使用了我们的第一个视图(按钮 背景色),并为其提供了 Frame。
如您所见,我们使用 ModifiedContent
类型堆叠——每个视图都需要一个视图进行转换以及要进行的实际更改,而不是直接修改视图。
这意味着修饰符的顺序很重要。 如果我们重写代码以便在设置 Frame 后使用背景色,那么您就会得到预期的结果:
代码语言:javascript复制Button("Hello World") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(Color.red)
现在最好的思考方法是,想象一下 SwiftUI
在每个修饰符之后都会呈现您的视图。因此,只要您说 .background(Color.red)
,它就会将背景颜色变为红色,而不管您给它什么 Frame
。如果您之后再扩展 Frame
,它将不会重新加载因为背景已经被使用了。
当然,这不是 SwiftUI
实际上的工作方式,因为如果这样做,那将是性能上的噩梦,但这是学习的时候可以使用的一种简洁的思维捷径。
使用修饰符的一个重要副作用是,我们可以多次应用相同的效果:每个修饰符都会简单地添加到以前的内容中。
例如,SwiftUI
为我们提供了 padding()
修饰符,该修饰符在视图周围添加了一些空间,从而不会将其推到其他视图或屏幕边缘。如果我们应用填充,然后应用背景色,然后应用更多填充和不同的背景色,则可以为视图提供多个边框,如下所示:
Text("Hello World")
.padding()
.background(Color.red)
.padding()
.background(Color.blue)
.padding()
.background(Color.green)
.padding()
.background(Color.yellow)
译自 Why modifier order matters[1]
参考资料
[1]Why modifier order matters: https://www.hackingwithswift.com/books/ios-swiftui/why-modifier-order-matters