为什么SwiftUI使用 some View 作为视图类型?
SwiftUI非常依赖于Swift的一个叫做“不透明返回类型(opaque return types)”的强大特性,您每次编写some View
时都可以看到它的实际作用。它意味着“一种符合View
协议的特定类型,但我们不想说它到底是什么。”
返回some View
与仅返回View
相比有两个重要区别:
1、我们必须始终返回相同类型的视图。
2、即使我们不知道返回的视图类型,编译器也同样不知道。
第一个区别对于性能很重要: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不让我们说“这个变量是数组”一样,而是要求我们说数组中的内容:“这个变量是字符串数组。(个人理解:一个数组只能是[String]
或者[Any]
或者其他,但是不能是[]
,同理这里的View
也是一样)
所以,它不允许写这样的视图:
struct ContentView: View {
var body: View {
Text("Hello World")
}
}
但写这样的视图是完全合法的:
struct ContentView: View {
var body: Text {
Text("Hello World")
}
}
返回View
是没有意义的,因为Swift想知道视图中的内容——它是一个必须填满的容器。另一方面,返回Text
是可以的,因为我们已经填充了这个容器,Swift知道视图是什么。
现在让我们回到前面的代码:
Button("Hello World") {
print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(Color.red)
如果我们想从我们的body
属性中返回其中一个,我们应该写些什么?虽然您可以尝试找出修ModifiedContent
泛型的确切组合,但这是非常痛苦的,而简单的事实是,我们不在乎:这都是SwiftUI内部的东西。
some View
让我们做的是说“这将返回一种特定类型的视图,如Button
或Text
,但我不想说它到底什么。”因此,视图容器将由一个真实的视图填充,但我们不需要写出确切的类型。
想进一步吗?
现在,如果你好奇的话,你可能会想,SwiftUI是如何处理VStack
这样的东西的——它符合View
协议,但是“它填充了什么样的内容?”,“容器里面能装很多不同的东西?
好吧,如果您创建了一个包含两个文本视图的VStack
,那么SwiftUI会无声地创建一个TupleView
来包含这两个视图——一种特殊类型的视图,其中正好包含两个视图。所以,“VStack
填充了什么样的视图?”回答:“这是一个包含两个文本视图的TupleView
。”
如果VStack
中有三个文本视图呢?然后是一个包含三个视图的TupleView
。或者四种观点。或者八个视图,甚至十个视图——TupleView
的一个版本可以跟踪十种不同的内容:
TupleView<(C0, C1, C2, C3, C4, C5, C6, C7, C8, C9)>
这就是为什么SwiftUI在一个父级中不允许超过10个视图的原因:他们编写了TupleView
的版本,可以处理2到10个视图,但不能超过10个(另一个版本:ForEach
使用的)。
Previous: 为什么SwiftUI的修饰符顺序很重要? | Hacking with iOS: SwiftUI Edition | Next: 条件修饰符 |
---|
赏我一个赞吧~~~