@ViewBuilder 是 SwiftUI 提供的属性包装器,用来构建多个视图,并将它们组合成单个返回值。它是 SwiftUI 处理视图层级的一种方式,尤其在声明式语法中,@ViewBuilder 提供了简洁且灵活的代码组织方式。
基本用法
@ViewBuilder的作用:允许在一个作用域中写出多个视图语句,不需要显式用容器包裹(VStack、Group)。
为了理解这一点,可以用实际的代码进行展示:
private struct testSettings: View {
func makeView() -> some View {
Text("Hello")
Text("World")
}
var body: some View {
makeView()
}
}
makeView方法显示两个Text视图,但是这个写法会报错:
Function declares an opaque return type, but has no return statements in its body from which to infer an underlying type
因为Swift要求一个函数必须返回单一的值,这里实际上返回了两个Text。
当设置return时,那么实际上只能返回一个Text视图:
private struct testSettings: View {
func makeView() -> some View {
Text("Hello") // 未返回
return Text("World")
}
var body: some View {
makeView()
}
}
因此,需要使用Group或者VStack等视图包裹返回:
private struct testSettings: View {
func makeView() -> some View {
Group {
Text("Hello")
Text("World")
}
}
var body: some View {
makeView()
}
}
这里返回的是Group视图,因此不需要显式的标记return关键字。
@ViewBuilder的作用是替代Group,也就是可以不使用Group、VStack等视图包裹的情况下,返回多个视图:
private struct testSettings: View {
@ViewBuilder
func makeView() -> some View {
Text("Hello")
Text("World")
}
var body: some View {
makeView()
}
}
这里就不会出现return返回单一值的报错。
这是因为 @ViewBuilder 会在编译阶段将这段代码转换为:
var body: some View {
TupleView((Text("Hello"), Text("World")))
}
也就是说@ViewBuilder自动组装多个视图为一个整体。
工作原理
@ViewBuilder 是一个特殊的 result builder(结果构建器)。
本质上,它是一个编译器魔法结构,大概定义如下(简化):
@resultBuilder
struct ViewBuilder {
static func buildBlock(_ components: some View...) -> some View {
TupleView(components)
}
}
当使用 @ViewBuilder 的作用域里写多个表达式时,编译器会自动调用 buildBlock 来合并这些视图。
@ViewBuilder 的局限性
虽然 @ViewBuilder 很强大,但它也有一些限制:
返回类型必须是 View:不能直接返回非视图类型。
嵌套层级:复杂的逻辑中可能需要用容器(如 Group)来辅助构建。
限制控制流:某些复杂控制流可能无法直接用 @ViewBuilder 表达。
使用场景
动态内容生成:根据条件分支生成不同的视图。
简化代码:避免显式地用 VStack、HStack 包裹多视图,代码更简洁。
封装视图逻辑:用于自定义容器组件,例如 SwiftUI 的 VStack 或 HStack 本质上也使用了类似的 @ViewBuilder。
注意事项
1、返回类型一致:条件语句中返回的视图类型需要一致。例如,不能在一个分支返回 Text,另一个分支返回 Image。
2、不能嵌套 @ViewBuilder:@ViewBuilder 不能直接嵌套使用。错误代码:
struct ContentView: View {
@ViewBuilder var body: some View {
@ViewBuilder // 错误:嵌套使用会报错
var innerView: some View {
Text("Hello")
}
innerView
}
}
报错代码为:
Underlying type for opaque result type 'some View' could not be inferred from return expression
表示无法从返回表达式中推断出不透明结果类型“some View”的底层类型。
3、@ViewBuilder 是一个“函数构建器”,只能修饰:
var body: some View(SwiftUI 特殊支持)
函数参数(如 init(@ViewBuilder content: () -> Content))
计算属性(@ViewBuilder var something: some View { … })
它不能修饰普通的存储属性(let content: View)。
总结
@ViewBuilderk可以将多个视图表达式在编译器合成一个View。
它可以修饰方法、变量或者body。
有人可能疑惑,为什么View的body没有添加@ViewBuilder仍然可以工作?
private struct testSettings: View {
var body:some View { // 没有使用@ViewBuilder
Text("Hello")
Text("World")
}
}
SwiftUI在View协议中的定义(简化版):
public protocol View {
associatedtype Body: View
@ViewBuilder var body: Self.Body { get }
}
body属性在协议定义时已经添加 @ViewBuilder。
所有遵循 View 协议的 struct,它的 body 都隐式带有 @ViewBuilder 行为。
扩展知识
1、@ViewBuilder修饰的函数和变量
@ViewBuilder既可以修饰函数:
@ViewBuilder func myContent() -> some View
也可以修饰变量:
@ViewBuilder var myContent: some View
两者都可以返回视图,区别在于函数可以传递参数,但是每次调用时都会创建新的视图;变量不可以传递参数,但是编译器可以缓存并优化。
@ViewBuilder
func sectionView(title: String) -> some View {
Text(title)
Divider()
}
@ViewBuilder
var footerView: some View {
Text("© 2025 Fang Junyu")
}
sectionView() 可以重用、动态生成。fotterView是固定视图。
