深入理解 SwiftUI 中的 @ViewBuilder:从语法糖到实战

原文链接:www.avanderlee.com/swiftui/vie...

什么是 @ViewBuilder?

@ViewBuilder 是 SwiftUI 提供的结果构建器(Result Builder) 之一,它让我们可以用 声明式语法 组合多个视图,而无需显式使用 return

在 SwiftUI 中,所有的视图协议 Viewbody 属性 都隐式使用了 @ViewBuilder,这也是为什么我们能在 body 里直接写多个视图而不需要 return

为什么需要 @ViewBuilder?

没有 @ViewBuilder 时会发生什么?

假设我们定义了一个 没有 @ViewBuilder 的协议:

swift 复制代码
protocol NonResultBuilderView {
    associatedtype Body: View
    var body: Self.Body { get }
}

如果我们尝试写一个 if-else 语句:

swift 复制代码
struct SalesFooterView: NonResultBuilderView {
    let isPro: Bool

    var body: some View {
        if isPro {
            Text("Hello, PRO user!")
        } else {
            Text("Hi there, don't you want to become PRO?")
        }
    }
}

如果 if-else 返回不同类型(如 TextButton),会报错:

swift 复制代码
struct SalesFooterView: NonResultBuilderView {
    let isPro: Bool

    var body: some View {
        if isPro {
            return Text("Hello, PRO user!")
        } else {
            return Button("Become PRO") {
                startPurchase()
            }
        }
    }

    func startPurchase() {
        // 购买逻辑
    }
}

错误:

Function declares an opaque return type, but the return statements in its body do not have matching underlying types.

修复方式:使用 @ViewBuilder

swift 复制代码
struct SalesFooterView: NonResultBuilderView {
    let isPro: Bool

    @ViewBuilder
    var body: some View {
        if isPro {
            Text("Hello, PRO user!")
        } else {
            Button("Become PRO") {
            }
        }
    }
}

@ViewBuilder 会自动将多个视图合并成一个 TupleView,从而解决类型不一致的问题。

@ViewBuilder 的 3 种使用方式

在初始化器中使用(类似 VStack/HStack

swift 复制代码
struct VHStack<Content: View>: View {
    @Environment(\.horizontalSizeClass) var horizontalSizeClass

    let content: Content

    init(@ViewBuilder _ content: () -> Content) {
        self.content = content()
    }

    var body: some View {
        if horizontalSizeClass == .compact {
            VStack { content } // 竖直布局
        } else {
            HStack { content } // 水平布局
        }
    }
}

使用方式:

swift 复制代码
struct ContentView: View {
    var body: some View {
        VHStack {
            Text("Hello, World!")
            Text("Result Builders are great!")
        }
    }
}

在属性上使用(更紧凑的写法)

swift 复制代码
struct VHStack<Content: View>: View {
    @Environment(\.horizontalSizeClass) var horizontalSizeClass
    @ViewBuilder var content: () -> Content // 直接标记属性

    var body: some View {
        if horizontalSizeClass == .compact {
            VStack(content: content) // 直接传入
        } else {
            HStack(content: content)
        }
    }
}

在方法上使用(条件修饰符)

swift 复制代码
extension Slider {
    @ViewBuilder
    func minimumTrackColor(_ color: Color) -> some View {
        if #available(macOS 11.0, *) {
            accentColor(color) // macOS 11+ 支持
        } else {
            self // 低版本保持原样
        }
    }
}

总结与扩展

核心要点

  1. @ViewBuilder 是 结果构建器,用于组合多个视图。
  2. SwiftUI 的 View.body 默认使用 @ViewBuilder,所以无需手动添加。
  3. 可以用于:
    • 初始化器(如 VStack { ... }
    • 属性(@ViewBuilder var content: () -> Content
    • 方法(@ViewBuilder func makeView() -> some View

进阶使用场景

✅ 动态列表(根据条件返回不同视图)

swift 复制代码
struct DynamicList: View {
    let items: [String]
    let isGrid: Bool

    @ViewBuilder
    var body: some View {
        if isGrid {
            LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))]) {
                ForEach(items, id: \.self) { Text($0) }
            }
        } else {
            List(items, id: \.self) { Text($0) }
        }
    }
}

✅ 空状态处理

swift 复制代码
struct SafeView<Content: View>: View {
    let isEmpty: Bool
    @ViewBuilder let content: () -> Content

    var body: some View {
        if isEmpty {
            Text("No data")
                .foregroundColor(.gray)
        } else {
            content()
        }
    }
}

✅ 条件动画

swift 复制代码
extension View {
    @ViewBuilder
    func conditionalAnimation(_ animate: Bool) -> some View {
        if animate {
            self.transition(.scale) // 带动画
        } else {
            self // 无动画
        }
    }
}

我的思考

@ViewBuilder 不仅仅是 语法糖,它让 SwiftUI 的 声明式语法 更加自然。

  • 对比 UIKit:以前我们用 if-else 动态布局时,需要手动管理视图层级,而 @ViewBuilder 让这一切变得 自动且类型安全。
  • 性能:虽然 if-else 可能会影响视图更新(SwiftUI 会重建视图),但在大多数情况下,它的开销可以忽略。
  • 未来方向:Swift 的 结果构建器 已经扩展到 Swift DSL(如 SwiftDataRegex),未来可能会有更多领域(如服务端 Swift)采用类似模式。
相关推荐
xqlily1 天前
Swift:现代、高效、安全的编程语言(二)
swift
z***y8621 天前
Swift在iOS中的Xcode
ios·xcode·swift
songgeb3 天前
iOS Audio后台模式下能否执行非Audio逻辑
ios·swift
东坡肘子5 天前
毕业 30 年同学群:一场 AI 引发的“真假难辨”危机 -- 肘子的 Swift 周报 #112
人工智能·swiftui·swift
Antonio9155 天前
【Swift】UIKit:UIAlertController、UIImageView、UIDatePicker、UIPickerView和UISwitch
ios·cocoa·swift
Antonio9155 天前
【Swift】UIKit:UISegmentedControl、UISlider、UIStepper、UITableView和UICollectionView
开发语言·ios·swift
1***81536 天前
Swift在服务端开发的可能性探索
开发语言·ios·swift
S***H2836 天前
Swift在系统级应用中的开发
开发语言·ios·swift
HarderCoder6 天前
SwiftUI 状态管理极简之道:从“最小状态”到“状态树”
swift
Antonio9156 天前
【Swift】 UIKit:UIGestureRecognizer和UIView Animation
开发语言·ios·swift