如果我想在 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
方法。
就这些了吗?是否存在某种方法在优化方面更好的情况?调试?特征?便携性?
@ViewBuilder
主要用于有条件地渲染视图,而不使用像 AnyView(YourView)
这样的解析器。引用Apple开发者网站:“此函数的客户端可以使用多语句闭包来提供多个子视图”。例如,如果您想根据变量显示视图,即如果某个变量为 true,则渲染 View1,如果为 false,则渲染 View2。
他们优先考虑不同的事情。例如,此代码获取锻炼传递的日期:
@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 似乎通常有多种方法来完成同一件事,这在决定使用哪种方法时可能会令人困惑,但最终让程序员可以根据更具体的需求定制代码。希望这有帮助!