SWIFTUI:为什么将 @State 添加到 属性 不会更新数据模型

SWIFTUI: Why adding @State to property doesn't update data model

更新 ObservedObject 中的数据时遇到问题 这是数据 MRE:

struct Property: Identifiable, Codable, Hashable {
    var id = UUID()
    var name : String = ""
    var meters : [Meter] = [Meter(name: "electricity"), Meter(name: "water")]
}
struct Meter: Identifiable, Hashable, Codable {
    var id = UUID()
    var name : String = ""
}

class PropertyData: ObservableObject {
    @Published var properties: [Property]
    
    
    func save() {
        let encoder = JSONEncoder()
        if let encoded = try? encoder.encode(properties) {
            UserDefaults.standard.set(encoded, forKey: "PropertyData")
        }
    }
    
    init() {
        if let properties = UserDefaults.standard.data(forKey: "PropertyData") {
            let decoder = JSONDecoder()
            if let decoded = try? decoder.decode([Property].self, from: properties) {
                self.properties = decoded
                return
            }
        }
        self.properties = [
            Property(name: "Saks /1", meters: [Meter(name: "electricity")]),
            Property(name: "Saks /2", meters: [Meter(name: "electricity"), Meter(name: "water")]),
        ]
    }
}


import SwiftUI

struct ContentView: View {
    @ObservedObject var data = PropertyData()
    
    var body: some View {
        NavigationView {
            List {
                ForEach(data.properties, id:\.self) {property in
                    NavigationLink(destination: NavigationLinkView(data: data, property: property)) {
                        Text(property.name)
                    }
                }
            }
        }
    }
}

struct NavigationLinkView: View {
    @ObservedObject var data : PropertyData
    var property:Property
    
    var body: some View {
        TabView {
            MetersView(data: data, property: property)
                .tabItem{
                    VStack {
                        Image(systemName: "scroll")
                        Text("Utitity")
                    }
                }
        }
    }
}

struct MetersView: View {
    @ObservedObject var data: PropertyData
    @State private var metersPage = 0
    @State var property : Property
    @State private var addMeters = false
    
    var body: some View {
        VStack {
            HStack {
                Picker("Meters", selection: $metersPage) {
                    ForEach (0 ..< property.meters.count, id:\.self) {index in
                        Text(property.meters[index].name)
                    }
                }
                .pickerStyle(SegmentedPickerStyle())
                Button{
                    print(property.meters)
                    addMeters.toggle()
                } label: {
                    Image(systemName: "gear")
                }
            }.padding()
            Spacer()
        }.sheet(isPresented: $addMeters){AddMetersView(data: data, property: $property)}
    }
}

struct AddMetersView: View {
    @ObservedObject var data : PropertyData
    @Binding var property : Property
    @State var newMeter: String = ""
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        VStack {
            Form{
                Section {
                    TextField("Add another meter", text: $newMeter)
                        .autocapitalization(.none)
                    Button{
                        if newMeter != "" {
                            property.meters.append(Meter(name: newMeter))
                            data.save()
                            print(property.meters.count)
                        }                    } label: {
                            Text("Add a meter")
                        }
                }
                ForEach(0..<property.meters.count, id:\ .self) {index in
                    Text(property.meters[index].name)
                }
                Section() {
                    Button("That's enough"){
                        print(property.meters)
                        presentationMode.wrappedValue.dismiss()}
                }
            }
        }
    }
}

我不明白,为什么我在 AddMetersView 上添加的仪表确实更新了仪表页面,但在我转到 ContentView 后就消失了。 另外,在我的应用程序中,如果我添加 属性,它会保留并持续存在,但 Meters

不会

这与您之前的问题类似。将 @State var property : Property 更改为

一切都与联系有关。你在几个地方打破了它们,我做了一些改变,并沿着这条线发表评论。

import SwiftUI

struct MetersParentView: View {
    //Change to StateObject
    @StateObject var data = PropertyData()
    
    var body: some View {
        NavigationView {
            List {
                ForEach($data.properties, id:\.id) {$property in
                    NavigationLink(destination: NavigationLinkView(data: data, property: $property)) {
                        Text(property.name)
                    }
                }
            }
        }
    }
}

struct NavigationLinkView: View {
    @ObservedObject var data : PropertyData
    //Make Binding there is no connection without it
    @Binding var property : Property
    
    var body: some View {
        TabView {
            MetersView(data: data, property: $property)
                .tabItem{
                    VStack {
                        Image(systemName: "scroll")
                        Text("Utitity")
                    }
                }
        }
    }
}

struct MetersView: View {
    @ObservedObject var data: PropertyData
    @State var selectedMeter: Meter = .init()
    //Change to Binding
    //State is a source of truth, it breaks the connection
    @Binding var property : Property
    @State private var addMeters = false
    
    var body: some View {
        VStack {
            HStack {
                Picker("Meters", selection: $selectedMeter) {
                    //Use object not index
                    ForEach(property.meters, id:\.id) {meter in
                        //Tag adjustment
                        Text(meter.name).tag(meter as Meter)
                    }
                }.onAppear(perform: {
                    selectedMeter = property.meters.first ?? Meter()
                })
                .pickerStyle(SegmentedPickerStyle())
                Button{
                    print(property.meters)
                    addMeters.toggle()
                } label: {
                    Image(systemName: "gear")
                }
            }.padding()
            Spacer()
        }.sheet(isPresented: $addMeters){
            AddMetersView(data: data, property: $property)
            
        }
    }
}

struct AddMetersView: View {
    @ObservedObject var data : PropertyData
    @Binding var property : Property
    @State var newMeter: String = ""
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        VStack {
            Form{
                Section {
                    TextField("Add another meter", text: $newMeter)
                        .autocapitalization(.none)
                    Button{
                        if newMeter != "" {
                            print(property.meters.count)
                            property.meters.append(Meter(name: newMeter))
                            print(property.meters.count)
                            data.save()
                            print(property.meters.count)
                        }
                        
                    } label: {
                        Text("Add a meter")
                    }
                }
                //Dont use index
                ForEach(property.meters, id:\ .id) {meter in
                    Text(meter.name)
                }
                Section() {
                    Button("That's enough"){
                        print(property.meters)
                        presentationMode.wrappedValue.dismiss()}
                }
            }
        }
    }
}
struct MetersParentView_Previews: PreviewProvider {
    static var previews: some View {
        MetersParentView()
    }
}