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

3

这是一个简单的问题,但是我可能在代码中忘记了某些东西。
让我们用一张图片更好地解释:

enter image description here

当选择或取消选项时,我基本上需要更新“COUNT”和“PRICE”。


我的代码结构如下:

型号

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

ToggleView

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{ $0.selected }.map{ $0.price }.reduce(0, +)

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

在这段代码中,我可以更新模型的selected状态,但包含所有模型并应刷新PRICE的视图模型没有更新
似乎数组中的模型没有改变。

我忘记了什么?

2个回答

2

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

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

注意:此处为内联编写,未经测试,可能需要修正一些拼写错误。

注:本文是对一个可能存在书写错误的内联代码段的说明。

不用担心错别字,我只需要修正建议,而不是复制/粘贴 :) - elp

1
如果你仔细观察,就会发现你创建了两个不同的单一真相来源。这就是父级未更新的原因,因为它没有与子级链接。
一种方法是将ServiceSelectorModel创建为结构体,并在子视图中将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{ $0.isSelected }.map{ $0.price }.reduce(0, +)
            
            Text("SELECTED: \(serviceSelectorVM.services.filter{ $0.isSelected }.count)")
            Text(String(format: "TOTAL PRICE: €%.2f", price))
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接