ObservableObject 不更新嵌套循环 SWIFTUI 中的视图

ObservableObject not updating view in nested loop SWIFTUI

关于以下项目:

您的 amountSum 为 100 当您点击一个用户的“加号”按钮时,该特定用户必须支付这笔金额,但如果您点击多个用户的“加号”按钮,则支付的金额将在他们之间平均分配。

知道如何在单击“加号”按钮时更新整个 Model2.MustPayM2 道具吗?

import SwiftUI

struct Model1: Identifiable, Codable {
    var id: String = UUID().uuidString
    var nameM1: String
    var amountM1: Double
    var amountSumM1: Double = 100
    var arrayM2: [Model2]
    var isVisible: Bool = false
}


struct Model2: Identifiable, Codable {
    var id: String = UUID().uuidString
    var nameM2: String
    var amountM2: Double = 0
    var mustPayM2: Bool = false
}


class ViewModel1: ObservableObject {
    
    @Published var Publi1: Model1
    @Published var Publi1s: [Model1] = []
    @Published var Publi2: Model2
    @Published var Publi2s: [Model2] = []

    init() {
        let pub2 = Model2(nameM2: "init")
        let pub1 = Model1(nameM1: "init", amountM1: 0, arrayM2: [pub2])
        
        self.Publi2 = pub2
        self.Publi1 = pub1
        
        var newPub1s: [Model1] = []
        for i in (0..<5) {
            let newNameM1 = "name\(i+1)"
            let newAmountM1 = Double(i+1)
            var newModel1 = Model1(nameM1: newNameM1, amountM1: newAmountM1, arrayM2: [pub2])
            var newPub2s: [Model2] = []
            for i in (0..<5) {
                let newNameM2 = "\(newNameM1)-user\(i+1)"
                let newModel2 = Model2(nameM2: newNameM2)
                newPub2s.append(newModel2)
            }
            newModel1.arrayM2 = newPub2s
            newPub1s.append(newModel1)
        }
        
        Publi1s = newPub1s
        Publi1 = newPub1s[0]
        Publi2s = newPub1s[0].arrayM2
        Publi2 = newPub1s[0].arrayM2[0]
    }

}


struct View1: View {
    
    @EnvironmentObject var VM1: ViewModel1
    
    @State private var tt: String = ""
    
    private let screenHeight = UIScreen.main.bounds.height
    
    var body: some View {
        ZStack {
            VStack {
                ForEach(0..<VM1.Publi2s.count, id: \.self) { i in
                    
                    Text("\(VM1.Publi2s[i].nameM2)")
                    
                    Text(tt)
                    
                    Button {
                        VM1.Publi2s[i].mustPayM2.toggle()
                        var a = VM1.Publi2s.filter { [=10=].mustPayM2 == true }
                        let b = VM1.Publi1.amountM1 / Double(a.count)
                        
                        // How can I update the new props between all users ??
                        // for j in 0..<a.count {
                        //     a[j].amountM2 = b
                        // }
                        
                    } label: {
                        Image(systemName: "plus")
                        
                    }
                }
                
                Spacer()
                
                Button {
                    VM1.Publi1.isVisible.toggle()
                } label: {
                    Text("SHOW ME")
                    
                }
                
                Spacer()
                
            }
            
            View2()
                .offset(y: VM1.Publi1.isVisible ? 0 : screenHeight)
        }
    }
}


struct View2: View {
    
    @EnvironmentObject var VM1: ViewModel1
    
    var body: some View {
        VStack {
            Spacer()
            ForEach(0..<VM1.Publi2s.count, id: \.self) { i in
                
                Text("\(VM1.Publi2s[i].amountM2)")
                
            }
        }
    }
}


struct View2_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            View1()
        }
        .environmentObject(ViewModel1())
    }
}

您的实施似乎过于复杂且容易出错。我实际上为此重写了代码。我添加了评论以明确我做了什么以及为什么做了某些事情。如果您不明白为什么,请不要犹豫,提出问题。但请先阅读并尝试理解代码。

//Create one Model containing the individuals
struct Person: Identifiable, Codable{
    var id = UUID()
    var name: String
    var amountToPay: Double = 0.0
    var shouldPay: Bool = false
}

//Create one Viewmodel
class Viewmodel:ObservableObject{
    //Entities being observed by the View
    @Published var persons: [Person] = []
    
    init(){
        //Create data
        persons = (0...4).map { index in
            Person(name: "name \(index)")
        }
    }
    
    //Function that can be called by the View to toggle the state
    func togglePersonPay(with id: UUID){
        
        let index = persons.firstIndex { [=10=].id == id}
        
        guard let index = index else {
            return
        }
        //Assign new value. This will trigger the UI to update
        persons[index].shouldPay.toggle()
    }
    
    //Function to calculate the individual amount that should be paid and assign it
    func calculatePayment(for amount: Double){
        //Get all persons wich should pay
        let personsToPay = persons.filter { [=10=].shouldPay }
        //Calcualte the individual amount
        let individualAmount = amount / Double(personsToPay.count)
        //and assign it. This implementation will trigger the UI only once to update
        persons = persons.map { person in
            var person = person
            person.amountToPay = person.shouldPay ? individualAmount : 0
            return person
        }
        
    }
}

struct PersonView: View{
    //pull the viewmodel from the environment
    @EnvironmentObject private var viewmodel: Viewmodel
    //The Entity that holds the individual data
    var person: Person
    var body: some View{
        VStack{
            HStack{
                Text(person.name)
                Text("\(person.amountToPay, specifier: "%.2f")$")
            }
            Button{
                //toggle the state
                viewmodel.togglePersonPay(with: person.id)
            } label: {
                //Assign label depending on person state
                Image(systemName: "\(person.shouldPay ? "minus" : "plus")")
            }
        }
    }
}

struct ContentView: View{
    //Create and observe the viewmodel
    @StateObject private var viewmodel = Viewmodel()
    var body: some View{
        VStack{
            //Create loop to display person.
            //Dont´t itterate over the indices this is bad practice
            // itterate over the items themselves
            ForEach(viewmodel.persons){ person in
                PersonView(person: person )
                    .environmentObject(viewmodel)
                    .padding(10)
            }
            
            Button{
                //call the func to calculate the result
                viewmodel.calculatePayment(for: 100)
            }label: {
                Text("SHOW ME")
            }
        }
    }
}