简体   繁体   English

ObservableObject 未在嵌套循环 SWIFTUI 中更新视图

[英]ObservableObject not updating view in nested loop SWIFTUI

Regarding the following project :关于以下项目:

You have an amountSum of 100 When you click on one user "plus" button, this specific user have to pay this amount but if you click on multiple user "plus" button, the amount to pay is divided between them equally.您的金额为 100 当您单击一个用户的“加号”按钮时,该特定用户必须支付此金额,但如果您单击多个用户的“加号”按钮,支付的金额将在他们之间平分。

Any idea how I can update the entire Model2.MustPayM2 prop when I click on the "plus" button please ?当我单击“加号”按钮时,知道如何更新整个 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 { $0.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())
    }
}

You implementation seems overly complicated and error prone.您的实现似乎过于复杂且容易出错。 I´ve practically rewritten the code for this.我实际上为此重写了代码。 I´ve added comments to make it clear what and why I have done certain things.我添加了评论,以明确我做了什么以及为什么做了某些事情。 If you don´t understand why, don´t hesitate to ask a question.如果你不明白为什么,不要犹豫,问一个问题。 But please read and try to understand the code first.但请先阅读并尝试理解代码。

//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 { $0.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 { $0.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")
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM