SwiftUI:struct View 与 @ViewBuilder

问题描述 投票:0回答:2

如果我想在 SwiftUI 中渲染项目列表,我可以这样做(使用 XCode 12):

struct MyView: View {
    let texts: [String]
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(texts.indices, id: \.self) { index in
                    MyRow(label: texts[index])
                }
            }
        }
    }

    struct MyRow: View {
        let label: String
        var body: some View {
            Text(label).font(.title3).padding()
        }
    }
}

MyRow
可以是一个函数,而不是一个结构体,这使得代码更加简洁和实用:

struct MyView: View {
    let texts: [String]
    var body: some View {
        ScrollView {
            LazyVStack {
                ForEach(texts.indices, id: \.self) { index in
                    MyRow(label: texts[index])
                }
            }
        }
    }

    @ViewBuilder func MyRow(label: String) -> some View {
        Text(label).font(.title3).padding()
    }
}

我想了解这两种方法之间的区别。 在某些情况下您会更喜欢其中一种吗?

首先想到的是,您不能将

@State
属性与函数一起使用,这意味着如果您的视图需要状态,则需要使用
struct
方法。

就这些了吗?是否存在某种方法在优化方面更好的情况?调试?特征?便携性?

swiftui
2个回答
1
投票

@ViewBuilder
主要用于有条件地渲染视图,而不使用像
AnyView(YourView)
这样的解析器。引用Apple开发者网站:“此函数的客户端可以使用多语句闭包来提供多个子视图”。例如,如果您想根据变量显示视图,即如果某个变量为 true,则渲染 View1,如果为 false,则渲染 View2。


0
投票

他们优先考虑不同的事情。例如,此代码获取锻炼传递的日期:

   @ViewBuilder private func workoutRow(_ workout: HKWorkout) -> some View {
        let date = workout.startDate.formatted(date: .abbreviated, time: .shortened)
        
        LabeledContent(workout.workoutActivityType.commonName,
                       value: date)
           
    }

但是当然,你不能使用状态。假设您想调用一个获取 energyBurned 的异步函数,您将其作为参数传递到锻炼中。

struct workoutRowView: View {
    @Environment(HKWrapper.self) var wrapper
    @State private var energyBurned: String = "Loading..."
    @State private var workout: HKWorkout

    var body: some View {
        VStack{
            Text(workout.workoutActivityType.commonName)
            Text(energyBurned)
        }
            .task {
                energyBurned = await wrapper.fetchEnergyBurned(for: workout)
            }
    }
}

您需要一个 @State 变量才能使用异步函数更新值。但在结构视图中,您不能只在顶部添加这一行:

let date = workout.startDate.formatted(date: .abbreviated, time: .shortened)

XCode 会给你一个错误,因为你不能在初始化锻炼时使用锻炼来初始化另一个值。因此,ViewBuilder 和 Swift Struct 的混合可以让您同时完成这两个任务。我还认为当视图中有一堆 if/else 时 ViewBuilder 会派上用场。

要完全回答您的问题“如果没有状态变量,为什么不总是使用始终视图生成器”,我不明白您为什么不能。但是,我认为结构体的使用更频繁,因为通常确实有某种状态变量。此外,Swift 似乎通常有多种方法来完成同一件事,这在决定使用哪种方法时可能会令人困惑,但最终让程序员可以根据更具体的需求定制代码。希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.