为什么 SwiftUI 使用 “some View” 作为视图类型?

SwiftUI 广泛使用了 Swift 中的一个强大功能——不透明返回类型(opaque return types)。当你写下 some View时,这个功能就开始发挥作用了,它的意思是:“一个符合 View 协议的对象,但我们不想明确指出它是什么类型。”

返回 some View 意味着,即使我们不知道具体返回的视图类型,编译器却知道。这看似细微,但却有着重要的影响。


1. 提升性能

使用 some View 对性能至关重要。
SwiftUI 需要能够检查我们显示的视图并了解它们是如何变化的,以便正确更新用户界面。如果没有这种额外信息,SwiftUI 就需要花费大量时间去弄清楚到底发生了什么变化——每次小改动都可能导致整个视图重新渲染。


2. View 协议与 ModifiedContent

SwiftUI 的视图是通过堆叠 ModifiedContent 类型来构建的。让我们回顾一下之前的代码:

Button("Hello World") {
    print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(.red)

这段代码定义了一个简单的按钮,并打印其确切的 Swift 类型。输出的类型中会包含几层 ModifiedContent,比如:

ModifiedContent<ModifiedContent<Button<Text>, _FrameLayout>, _BackgroundStyleModifier<Color>>

Swift 的 View 协议带有一个关联类型(associated type),这意味着 View 本身并不是一个具体的类型——我们需要明确指定视图的具体类型。这与 Swift 数组的工作方式类似:Swift 不允许我们仅仅声明 “这是一个数组”,而是要求我们说明数组中存放的具体类型,比如 Array<String>

因此,这种写法是不合法的:

struct ContentView: View {
    var body: View {
        Text("Hello, world!")
    }
}

因为 View 是一个“未填充的洞”,编译器需要知道具体的视图类型。然而,这种写法是合法的:

struct ContentView: View {
    var body: Text {
        Text("Hello, world!")
    }
}

在这里,Text 填充了 View 的“洞”,让 Swift 知道了视图的具体类型。


3. 为什么需要 some View

回到刚才的代码:

Button("Hello World") {
    print(type(of: self.body))
}
.frame(width: 200, height: 200)
.background(.red)

如果我们希望返回这种视图,应该如何声明类型?
虽然可以尝试手动写出 ModifiedContent 结构的确切组合,但这极为繁琐且毫无意义,因为这些是 SwiftUI 的内部实现,开发者并不关心。

some View 允许我们这样写:“这将是一个视图,比如 Button 或 Text,但我不想明确指定具体类型。”因此,View 中的“洞”会由一个具体的视图对象填充,但我们无需手动写出确切的类型。


4. 复杂情况:VStack 和多视图返回

(1) VStack 的内容类型

如果我们在 VStack 中放入两个文本视图,SwiftUI 会默默地创建一个 TupleView,用来包含这两个视图——这是一个特殊的视图类型,可以容纳正好两个视图。

当 VStack 中有三个文本视图时,它会变成包含三个视图的 TupleView。如果有四个视图、八个视图,甚至十个视图,TupleView 就会继续扩展,以容纳所有视图。

(2) 多个视图直接返回

如果我们在 body 属性中直接返回多个视图,没有用堆叠视图(如 VStack)包裹它们,Swift 会自动应用一个名为 @ViewBuilder 的特殊属性。这会将多个视图隐式地包装成一个 TupleView 容器,因此看似我们返回了多个视图,实际上它们被组合成了一个。

如果你右键点击 View 协议并选择 “Jump to Definition”,可以看到 body 属性的定义中带有 @ViewBuilder 属性:

@ViewBuilder @MainActor var body: Self.Body { get }

这个行为不是魔法,而是 SwiftUI 通过类型系统和编译器特性实现的。虽然 SwiftUI 并未在文档中明确定义这种行为,但这种灵活性对开发者来说非常有用。


总结来说,some View 是 SwiftUI 的核心工具,它让我们可以灵活返回符合 View 协议的视图,同时隐藏具体实现细节。这既提升了性能,也简化了开发者的代码书写体验。

Review after registration

login page