SwiftUI - 刷新视图模型数组中更新的模型视图

SwiftUI - Refresh view on model updated in view model array

要问的问题很简单,但我可能忘记了代码中的某些内容。
让我们用一张图片更好地解释一下:

我基本上需要更新“COUNT”和“PRICE”,而 selecting/deselecting 项。


我有这样的代码结构:

型号:

class ServiceSelectorModel: Identifiable, ObservableObject {
    var id = UUID()

    var serviceName: String
    var price: Double
    
    init(serviceName: String, price: Double) {
        self.serviceName = serviceName
        self.price = price
    }

    @Published var selected: Bool = false
}

ViewModel:

class ServiceSelectorViewModel: ObservableObject {
    @Published var services = [ServiceSelectorModel]()

    init() {
        self.services = [
            ServiceSelectorModel(serviceName: "SERVICE 1", price: 1.80),
            ServiceSelectorModel(serviceName: "SERVICE 2", price: 10.22),
            ServiceSelectorModel(serviceName: "SERVICE 3", price: 2.55)
        ]
    }
}

切换视图

struct ServiceToggleView: View {
    @ObservedObject var model: ServiceSelectorModel
    
    var body: some View {
        VStack(alignment: .center) {
            HStack {
                Text(model.serviceName)
                
                Toggle(isOn: $model.selected) {
                    Text(String(format: "€ +%.2f", model.price))
                        .frame(maxWidth: .infinity, alignment: .trailing)
                }
            }
            .background(model.selected ? Color.yellow : Color.clear)
        }
    }
}

ServiceSelectorView

struct ServiceSelectorView: View {

    @ObservedObject var serviceSelectorVM = ServiceSelectorViewModel()

    var body: some View {
        VStack {
            VStack(alignment: .leading) {
                ForEach (serviceSelectorVM.services, id: \.id) { service in
                    ServiceToggleView(model: service)
                }
            }

            let price = serviceSelectorVM.services.filter{ [=13=].selected }.map{ [=13=].price }.reduce(0, +)

            Text("SELECTED: \(serviceSelectorVM.services.filter{ [=13=].selected }.count)")
            Text(String(format: "TOTAL PRICE: €%.2f", price))    
        }
    }
}

在此代码中,我能够更新模型的 selected 状态,但包含所有模型的视图模型应该刷新 PRICE 不更新.
似乎数组中的模型没有改变。

我忘记了什么?

可能最简单的方法是将您的模型设为值类型,然后更改其属性,持有它的视图模型将被更新。并更新视图以使用绑定到这些值

struct ServiceSelectorModel: Identifiable {
    var id = UUID()

    var serviceName: String
    var price: Double
    
    init(serviceName: String, price: Double) {
        self.serviceName = serviceName
        self.price = price
    }

    var selected: Bool = false
}

struct ServiceToggleView: View {
    @Binding var model: ServiceSelectorModel
...
}

...

ForEach (serviceSelectorVM.services.indices, id: \.self) { i in
    ServiceToggleView(model: $serviceSelectorVM.services[i])
}

注意:内联编写,未经测试,可能需要修复一些错字

如果您仔细观察,您已经创建了两个不同的单一事实来源。这就是 parent 没有更新的原因,因为它没有链接到 child。

一种方法是将 ServiceSelectorModel 创建为结构,并在 child 视图中将 services 数组作为 @Binding 传递。下面是工作示例。

ViewModel-:

struct ServiceSelectorModel {
    
    var id = UUID().uuidString
    var serviceName: String
    var price: Double
    var isSelected:Bool = false
    
    init(serviceName: String, price: Double) {
        self.serviceName = serviceName
        self.price = price
    }
    
}

class ServiceSelectorViewModel: ObservableObject,Identifiable {
    
    @Published var services = [ServiceSelectorModel]()
    
    var id = UUID().uuidString
    init() {
        self.services = [
            ServiceSelectorModel(serviceName: "SERVICE 1", price: 1.80),
            ServiceSelectorModel(serviceName: "SERVICE 2", price: 10.22),
            ServiceSelectorModel(serviceName: "SERVICE 3", price: 2.55)
        ]
    }
}

观看次数-:

struct ServiceToggleView: View {
    @Binding var model: [ServiceSelectorModel]
    
    var body: some View {
        VStack(alignment: .center) {
            ForEach(0..<model.count) { index in
                HStack{
                    Text(model[index].serviceName)
                    Toggle(isOn: $model[index].isSelected) {
                        Text(String(format: "€ +%.2f", model[index].price))
                            .frame(maxWidth: .infinity, alignment: .trailing)
                    }
                }.background(model[index].isSelected ? Color.yellow : Color.clear)
            }
            
        }
    }
}

struct ServiceSelectorView: View {
    
    @ObservedObject var serviceSelectorVM = ServiceSelectorViewModel()
    
    var body: some View {
        VStack {
            VStack(alignment: .leading) {
                ServiceToggleView(model: $serviceSelectorVM.services)
            }
            
            let price = serviceSelectorVM.services.filter{ [=11=].isSelected }.map{ [=11=].price }.reduce(0, +)
            
            Text("SELECTED: \(serviceSelectorVM.services.filter{ [=11=].isSelected }.count)")
            Text(String(format: "TOTAL PRICE: €%.2f", price))
        }
    }
}