更多内容欢迎关注公众号 「Swift花园」
无论何时,当我们应用一个 modifier 到 SwiftUI 视图时,我们实际上是创建了应用一个应用了改变的新视图 —— 我们并不是在修改已经存在的视图。认真琢磨一下,这个机制很合理 —— 我们的视图只持有我们赋予他们的属性。因此,如果我们设置背景色或者字号,没有额外的地方存储这些数据。
在后续的章节中我们会讨论背后究竟发生了什么,这里我们先看一看这个行为背后隐含的信息。请看下面的代码:
Button("Hello World") {
// do nothing
}
.background(Color.red)
.frame(width: 200, height: 200)你认为运行起来会是什么样子呢?
有可能你会猜错:你不会看到一个 200x200 的红色按钮,中间是 “Hello World” 文字。相反,你会看到一个 200x200 的空的矩形,"Hello World" 在中间,而红色矩形只出现在 "Hello World" 周围。就像下面这样:
如果你思考过 modifier 的工作方式,你就会理解发生了什么:每个 modifier 都创建了新的结构体,而非在原有视图上设置属性。
你可以借由打印视图的 body 的类型来一窥 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> 来应用 modifier 。
- 当我们应用了多个 modifier 时,它们层层叠加:ModifiedContent<ModifiedContent<…
为了读懂这个类型,我们从最深处开始:
- 最深处的类型是 ModifiedContent<Button<Text>, _BackgroundModifier<Color>: 带文本的按钮,应用一个背景色。
- 在它周围是 ModifiedContent<…, _FrameLayout>,它取刚才那个视图作为第一个参数,然后加一个更大的 frame 。
如你所见,我们最后会得到一些叠加在一起的ModifiedContent —— 其中的每一层都接收一个视图,然后添加一个改变完成变换,而非直接修改视图。
这意味着,你的 modifier 的顺序至关重要。 如果我们把代码重写成下面这样,在 frame 之后应用背景色,那么你将得到之前你所期望的视觉效果:
Button("Hello World") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(Color.red)效果:
考虑这个机制,最佳的方式是想象 SwiftUI 在每一个 modifier 之后渲染你的视图。因此,只要当你说 .background(Color.red) 它就把背景填充成红色。如果你之后又延展了 frame ,它并不会魔法般地自动重绘背景 —— 它之前已经应用过了。
当然, SwiftUI 其实不是这么工作的。因为如果真的是这样,那会造成性能的噩梦,但在学习的过程中,这可以作为一个认知的捷径。
使用 modifier 的一个重要的副作用是我们可以重复地应用相同的效果多次:每一个都只是在前面的基础上叠加。
例如,SwiftUI 提供给我们 padding() modifier ,它会在视图周围添加一些留白,以便视图不与其他视图或者屏幕的边缘靠在一起。如果我们先应用了 padding 然后再应用 background color ,然后是新的 padding 和不一样的 background color ,我们将会得到一个拥有多层边框的视图,代码如下:
Text("Hello World")
.padding()
.background(Color.red)
.padding()
.background(Color.blue)
.padding()
.background(Color.green)
.padding()
.background(Color.yellow)感受一下效果:
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~