为什么数组实例 var 的 SwiftUI 绑定不起作用?

问题描述

绑定不符合预期并且 我很想了解正在发生的事情。

这是说明问题的代码一个名为“Project”的类模型对象 包含一个名为“name”的字符串数组。 代码name 的 Binding 传递给 ProjectVM 类型的 viewmodel 在视图中使用。在视图列表中,我可以删除一行, 对应于删除字符串数组的元素之一, 但随后它又回来了。

这段代码应该在原始数组上运行,因为它是 使用绑定,但显然这不是正在发生的事情。有什么想法吗?

如果根对象是名称的@State var,它会按预期工作(请参阅 注释掉的代码)而不是项目的属性

在 Swift 5 中使用 Xcode 12.4

@main
struct Try_ArrayBindingApp: App {
    @State var project = Project()
    //@State var names = [ "a","b","c" ]
    var body: some Scene {
        WindowGroup {
            ProjectV(pVM: ProjectVM(names: $project.names))
            //ProjectV(pVM: ProjectVM(names: $names))
        }
    }
}

class Project { var names = [ "one","two","three"] }

class ProjectVM: ObservableObject {
    @Binding var names: [String]
    
    init(names: Binding<[String]> ) { self._names = names }
    
    func delete(at offsets: IndexSet) {
        names.remove(atOffsets: offsets)
    }
}

struct ProjectV: View {
    @Observedobject var pVM: ProjectVM
    
    var body: some View {
        vstack {
            List {
                ForEach(pVM.names,id: \.self) { n in
                    Text(n)
                }
                .onDelete(perform: delete)
            }
        }
    }
    
    private func delete(at offsets: IndexSet) {
        pVM.delete(at: offsets)
    }
}

解决方法

通过在父视图中保存初始@State,然后将其@Binding 到 observable 对象,然后将其发送到子视图,至少,数据流肯定会变得混乱。我实际上并不相信它不应该表现得像你想象的那样,但这是一个令人困惑的思维模型,而不是你在 SwiftUI 中经常看到的东西。

更常见的模型是将状态保存在 ObservableObject 中,该对象归父视图所有:

@main
struct Try_ArrayBindingApp: App {
    @StateObject var project = ProjectVM(names: [ "one","two","three"])
    var body: some Scene {
        WindowGroup {
          ProjectV(pVM: project)
        }
    }
}

class ProjectVM: ObservableObject {
    @Published var names: [String]
    
    init(names: [String]) {
        self.names = names
    }
    
    func delete(at offsets: IndexSet) {
        names.remove(atOffsets: offsets)
    }
}

struct ProjectV: View {
    @ObservedObject var pVM: ProjectVM
    
    var body: some View {
        VStack {
            List {
                ForEach(pVM.names,id: \.self) { n in
                    Text(n)
                }
                .onDelete(perform: delete)
            }
        }
    }
    
    private func delete(at offsets: IndexSet) {
        pVM.delete(at: offsets)
    }
}

请注意,names 现在是 @Published 属性。