因为wiftUI 高度依赖 Swift 5.1 引入的一个强大特性,它叫 “opaque return types” ,它可以用于函数、方法和属性返回一些值,无需向调用API的客户端揭示该值的具体类型。每一次你看到 some View 的地方就是它了。
一、为什么 SwiftUI 用 “some View” 作为视图类型
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生命周期
SwiftUI同UIKit和AppKit的主要区别之一是,SwiftUI的视图(View)是值类型,并不是对屏幕上绘制内容的具体引用。在SwiftUI中,开发者为视图创建描述,而并不实际渲染它们。
在UIKit(或AppKit)中,视图(或视图控制器)有明确的生命周期节点,比如vidwDidload、loadView、viewWillAppear、didAddSubView、didMoveToSuperview等方法,它们本质上充当了钩子的角色,让开发者能够通过执行一段逻辑来响应系统给定的事件。
SwiftUI的视图,本身没有清晰(可适当描述)的生命周期,它们是值、是声明。SwiftUI提供了几个修改器(modifier)来实现类似UIKit中钩子方法的行为。比如onAppear同viewWillAppear的表现很类似。同UIKit的钩子方法的位置有很大的不同, onAppear和onDisappear是在当前视图的父视图上声明的。
将UIKit视图包装成SwiftUI的视图时,我们需要了解两者生命周期之间的不同,不要强行试图找到完全对应的方法,要从SwiftUI的角度来思考如何调用UIKit视图。