【问题标题】:SwiftUI: How to know change in the published var of an ObservableObjectSwiftUI:如何知道 ObservableObject 的已发布变量的变化
【发布时间】:2022-10-20 10:33:08
【问题描述】:

我有一个基于模型数据显示切换列表的视图。这个模型有一个@Published 变量,它会根据切换选择进行更改。

class Model: ObservableObject, Hashable {
      
    var id: String
    @Published var isSelected: Bool
    
    init(id: String, isSelected: Bool) {
        self.id = id
        self.isSelected = isSelected
    }
    ...
}

class ViewModel: ObservableObject {

    var list: [Model]
    
    init() { ...}
    ...
    
    func save() {
        
    }
    
    func clear() {
        
    }
}


struct MyView: View {

    @ObservedObject var viewModel: ViewModel

    var body: some View {
        VStack {
            ForEach(viewModel.list, id: \.self) { model in
                Toggle(model.id, isOn: $model.isSelected)
            }
            Button("Done") {
                viewModel.save()
            }
            Button("Clear") {
                viewModel.clear()
            }
            ...
        }
    }
}

问题是,因为我有一系列带有@Publsihed 的模型,

  • 如何知道用户是否更改了任何切换,所以我可以启用/禁用保存按钮
  • 如何知道列出所有已更改的切换(即模型 isSelected 已更改),例如当我按保存时

【问题讨论】:

  • 一些观察:不要像你一样嵌套ObservableObject,它会产生问题。重新构建您的代码以拥有struct Model:...var isSelected: Bool,并在ViewModel 中拥有@Published var list: [Model]
  • 我建议尝试将您的问题分开(在不同的帖子中),以便他们可以专注于一件事,并且更容易回答。

标签: ios swift swiftui combine


【解决方案1】:

尝试类似这种方法,以更改模型列表 isSelected 属性。

struct Model: Identifiable, Hashable {
    var id: String
    var isSelected: Bool
    var hasChanged: Bool = false // <--- here
}

class ViewModel: ObservableObject {
    
    // for testing
    @Published var list: [Model] = [Model(id: "1", isSelected: false),
                                    Model(id: "2", isSelected: false),
                                    Model(id: "3", isSelected: true)]
    
     func save() {
         for mdl in list {
             if mdl.hasChanged {
                print("---> (mdl.id) has changed")  // <-- here
             }
         }
     }
    
    func clear() {
        list = []
        print("---> clear list: (list)")
    }
    
    func reset() {
        for i in list.indices {
            list[i].isSelected = false
        }
        print("---> reset list: (list)")
    }
    
}

struct ContentView: View {
    @StateObject var viewModel = ViewModel()
    
    var body: some View {
        MyView(viewModel: viewModel)
    }
}

struct MyView: View {
    @ObservedObject var viewModel: ViewModel
    @State var hasChanged = false
    
    var body: some View {
        VStack {
            ForEach($viewModel.list) { $model in
                Toggle(model.id, isOn: $model.isSelected)
                    .onChange(of: model.isSelected) { val in  // <-- here
                        model.hasChanged.toggle()
                    }
            }
            Button("Save") {
                viewModel.save()
            }
            
            Button("Clear") {
                viewModel.clear()
            }
            Button("Reset") {
                viewModel.reset()
            }
        }
    }
}

编辑-1:

如果要计算特定Model 更改的次数,则 只需为其添加一个计数器,例如在此示例代码中:

struct MyView: View {
    @ObservedObject var viewModel: ViewModel
    
    var body: some View {
        VStack {
            ForEach($viewModel.list) { $model in
                Toggle(model.id, isOn: $model.isSelected)
                    .onChange(of: model.isSelected) { val in  
                        model.hasChanged.toggle()
                        model.changeCount += 1    // <-- here
                    }
            }
            Button("Save") {
                viewModel.save()
            }
            
            Button("Clear") {
                viewModel.clear()
            }
            Button("Reset") {
                viewModel.reset()
            }
        }
    }
}

struct Model: Identifiable, Hashable {
    var id: String
    var isSelected: Bool
    var hasChanged: Bool = false 
    var changeCount: Int = 0  // <--- here
}

class ViewModel: ObservableObject {
    
    // for testing
    @Published var list: [Model] = [Model(id: "1", isSelected: false),
                                    Model(id: "2", isSelected: false),
                                    Model(id: "3", isSelected: true)]
    
    func save() {
        for var mdl in list {
            print("---> (mdl.id) has changed = (mdl.hasChanged)  count: (mdl.changeCount)")  // <-- here
        }
    }
    
    func clear() {
        list = []
        print("---> clear list: (list)")
    }
    
    func reset() {
        for i in list.indices {
            list[i].isSelected = false
        }
        print("---> reset list: (list)")
    }
    
}

【讨论】:

  • 感谢您的回答。这是我认为的第二个问题。在这里,我只需要知道那些被改变的。一种选择是进行本地复制和比较,但看看是否有比这更好的方法。也许我应该更清楚我的问题。
  • 用您的其他问题的可能解决方案更新了我的答案...to know only the one's which are changed...
  • @workingdogsupportUkraine 如何跟踪同一个对象是否多次更改。 (我们只能在第一次使用 hasChanged 进行检查)
  • 我的代码的逻辑是这样的:如果ModelisSelected 属性发生了变化(使用切换),hasChanged 将被设置为true。如果您将isSelected 改回原来的样子,hasChanged 将设置为false,因为Model 现在又恢复到原来的样子,也就是说它没有改变。如果您更改isSelected再次hasChanged 将设置为true再次.它随时可用,而不仅仅是第一次。
  • 用找零计数器更新了我的答案。
猜你喜欢
  • 2021-10-03
  • 1970-01-01
  • 2023-04-08
  • 2020-11-26
  • 2020-12-05
  • 2019-06-29
  • 2014-09-02
  • 2022-07-02
  • 2021-08-15
相关资源
最近更新 更多