问题描述
我有一个列表,它的单元格内容绑定到一个模型。模型内容来自网络。我使用@StateObject + @ObservableObject + @Published 机制来应用绑定如下:
视图模型是:
class viewmodel: ObservableObject {
@Published var employees = [Employee]()
}
视图是:
import SwiftUI
struct PresentView: View {
@StateObject var viewmodel = Presenterviewmodel("https://www.somelink.com/")
var body: some View {
NavigationView {
List(self.viewmodel.employees) { employee in
EmployeeView(name: employee.name,role: employee.title.rawValue)
}
.listStyle(GroupedListStyle())
.onAppear() {
viewmodel.load() // URL combine fetch
}
.navigationBarTitle("Company")
}
}
}
struct EmployeeView: View {
var name: String // <---- I want to attribute it a @Binding
var role: String // <---- I want to attribute it a @Binding
var body: some View {
HStack {
Image(imageName)
vstack {
Text("\(name)")
.font(.headline)
Text("\(role)")
.font(.subheadline)
}
}
}
}
('Employee' 携带一些字符串和一个枚举并符合 Identifiable 和 Decodable)
我的问题:
为什么我不能将 EmployeeView 的属性声明为 @Binding? (它喊道:“无法将 'String' 类型的值转换为预期的参数类型 'Binding”,我猜他期待来自调用者的“@State”,但我无法在这里提供)。 毕竟应用@Binding 是有意义的:我的视图模型中已经有了一个真实来源和一个双向绑定。如果我让 EmployeeView 没有 @Binding,这意味着每个 List 加载都会复制每个单元格内容,这是多余的,因为我的模型中已经有了真实的来源。 我做错了什么?
解决方法
毕竟应用@Binding 是有意义的:我的视图模型中已经有了真实来源和双向绑定。
这不是正确的思考方式。如果子视图没有修改内容,则不需要绑定。是的,数据会被复制,但这不会破坏事实来源 - 事实来源仍然是视图模型,无论那里有多少不可变副本
但是如果子视图正在修改内容,那么您需要使用 @Binding
注释它修改的属性,并且您需要将 Binding<String>
传递给它们。现在您直接传递 String
- 因此出现错误。
在 SwiftUI 中绑定列表项有点奇怪(在我看来)。您可以采取多种方法。其中之一是迭代数组的索引而不是元素,并使用 $employees[index]
获取对元素的绑定。
我还建议将整个 employee
对象传递给 EmployeeView
而不是它的单个属性。
List(self.viewModel.employees.indices,id: \.self) { index in
EmployeeView(employee: self.$viewModel.employees[index])
}
然后,您需要在 @Binding
的属性上使用 EmployeeView
:
struct EmployeeView: View {
@Binding var employee: Employee
var body: some View {
VStack {
TextField("",text: $employee.name) // ex: modifies the name
Text(employee.role) // ex: doesn't modify the role
}
}
}