【问题标题】:Bind List's cell property to an Observable ViewModel将 List 的单元格属性绑定到 Observable ViewModel
【发布时间】:2021-07-01 11:56:09
【问题描述】:

我有一个列表,它的单元格内容绑定到一个模型。模型内容取自网络。我使用@StateObject + @ObservableObject + @Published 机制来应用绑定如下:

ViewModel 是:

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 and Decodable)

我的问题:

为什么我不能将 EmployeeView 的属性声明为@Binding? (它喊道:“无法将'String'类型的值转换为预期的参数类型'Binding”,我猜他期待调用者的“@State”,但我不能在这里提供)。 毕竟应用@Binding 是有意义的:我的视图模型中已经有了一个事实来源和一个双向绑定。如果我在没有 @Binding 的情况下保留 EmployeeView,这意味着每个单元格内容都将针对每个 List 加载进行复制,这是多余的,因为我的模型中已经有了事实来源。 我做错了什么?

【问题讨论】:

  • 既然EmployeeView 没有修改任何东西,为什么你需要它成为Binding

标签: swiftui swiftui-list


【解决方案1】:

毕竟,应用@Binding 是有意义的:我已经在我的视图模型中拥有了事实来源和双向绑定。

这不是正确的思考方式。如果子视图不修改内容,则不需要绑定。是的,数据会被复制,但这不会破坏事实的来源 - 事实的来源仍然是视图模型,无论那里有多少 不可变 副本是。


但如果子视图正在修改内容,那么您需要使用@Binding 注释它修改的属性,并且您需要将Binding&lt;String&gt; 传递给它们。现在您正在直接传递 String - 因此出现错误。

在 SwiftUI 中绑定一个列表项(在我看来)有点奇怪。您可以采取多种方法。其中之一是迭代数组的索引,而不是元素,并使用$employees[index] 来获取到元素的绑定。

我还建议将整个 employee 对象传递给 EmployeeView,而不是其各个属性。

List(self.viewModel.employees.indices, id: \.self) { index in
   EmployeeView(employee: self.$viewModel.employees[index])
}

那么,你需要在EmployeeView的属性上使用@Binding

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
      }
   }
}

【讨论】:

  • 尊敬的新开发人员 感谢您的意见,我喜欢将整个对象作为参考传递的想法。是的,它不会显示在代码上,但我计划在未来更改内容,以便您的解决方案可以正常工作。 10X
猜你喜欢
  • 2019-02-28
  • 2011-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-18
  • 1970-01-01
  • 2017-08-08
相关资源
最近更新 更多