更多内容欢迎关注公众号 「Swift花园」
SwiftUI 高度依赖 Swift 5.1 引入的一个强大特性,它叫 “opaque return types” ,它可以用于函数、方法和属性返回一些值,无需向调用API的客户端揭示该值的具体类型。每一次你看到 some View 的地方就是它了。它表示 “某个遵循View协议的特定类型,但我们不必说具体是什么”
返回 some View 相较只返回 View 有两个重要的区别:
- 我们必须总是返回相同的类型。
- 尽管我们并不知道返回的 view 的类型,但编译器知道。
第一个区别对于性能至关重要:SwiftUI 需要能够监视我们正在展示的视图并理解它们如何变化,以便它能正确地更新用户接口。如果我们允许随机地改变视图(类型),对于 SwiftUI 来说,搞清楚究竟发生了什么变化会变得很慢 —— 基本上需要从头开始。
第个区别也很重要,因为 SwiftUI 用 ModifiedContent 构建起数据。之前我们向你展示过下面这份代码:
Button("Hello World") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(Color.red)这个代码创建了一个按钮,如果打印它的 Swift 类型,会给出一组由 ModifiedContent 构成的很长的输出。
View 协议有一个与之关联的类型。以 Swift 的方式,我们说 View本身没有任何含义—— 我们需要指出它精确的类型。就如果 Swift 不让我们说 “这个变量是个数组”,相反,它要求我们说数组里有什么:“这个变量是个字符串数组。”
因此,如下代码是不被允许的:
struct ContentView: View {
var body: View {
Text("Hello World")
}
}但下面的代码则是完全合法的:
struct ContentView: View {
var body: Text {
Text("Hello World")
}
}返回 View 没有意义,因为 Swift 希望知道视图里有什么 —— 它内心的空洞没被填满。另一方面,返回 Text 就是 OK 的,因为我们填补了这个空洞,Swift 知道视图是什么了。
现在我们回到早期的代码:
Button("Hello World") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(Color.red)如果我们想要在body 属性里返回上面任何一个视图类型,我们应该怎么书写代码?你可能会尝试搞清楚这些 ModifiedContent 泛型组合的结果,但这实际上太痛苦了,事实上我们根本不用关系,这些都是 SwiftUI 的活。
some View 说的是:“它将返回一个特定的 view 类型,比如Button 或者 Text,但我不想说具体是啥。” 因此,视图的空洞会被一个真实的视图填充,但不要求我们精确地写出这个冗长的类型。
更进一步
现在,如果你很好奇,你可能会像知道 SwiftUI 如果能处理像VStack这样的东西 —— 它遵循 View 协议,但它是怎么填充 “它有什么样的内容?” 这样的空洞呢?如果它可以包含非常多不同的东西。
是这样的,如果你创建一个 VStack ,里面有两个文本视图,SwiftUI 会静默地创建一个TupleView ,包含这两个文本视图 —— 这是一种特殊的视图,它只包含两个视图在里面。因此,VStack实际上是以包含两个文本视图的TupleView 来回答那个问题。
如果 VStack里有三个文本视图呢? 那么就会是一个包含三个视图的 TupleView,或者 4 个视图,8个视图,甚至 10 个视图 —— 确实有可以追踪 10 个不同类型内容的TupleView 版本:
TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>这也是为什么 SwiftUI 不允许一个父节点拥有超过 10 个视图:他们写了可以处理 2 个视图到 10 个视图的 TupleView 版本,但不支持更多了。
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~