简体   繁体   中英

ObservedObject List Update

i'm try to learn SwiftUI, i'm try to update my list automatically once I insert the value. but i'm getting a big issue, my list not update when I use a sheet or a navigation view to insert data. it only work if I load data from my contentView.

(and I don't understand why, the class DataManager is ObservableObject and more over it work perfectly if I load data with 3 textfield in the content view)

here below my project: I have a Data Model


import Foundation
import Combine

class DataModel: Codable, Identifiable {
    var id: UUID = UUID()
    var airportName : String
    var metar : String
    var taf : String

init(airportName: String, metar: String, taf: String) {
    self.airportName = airportName
    self.metar = metar
    self.taf = taf
}
}

I have a DataManager

import SwiftUI
import Combine

class DataManager: ObservableObject {
    let objectWillChange = PassthroughSubject<Void, Never>()


    var storage : [DataModel] = [] {
        willSet {
            objectWillChange.send()
        }
    }
    typealias Storage = [DataModel]

    var filePath : String = ""

    init() { caricaDati() }

    func caricaDati() {
        // creiamo il percorso al file
        filePath = cartellaDocuments() + "test.plist"

        // usiamo NSFileManager per sapere se esiste un file a quel percorso
        if FileManager.default.fileExists(atPath: filePath) {

            // se c'è de-archiviamo il file di testo nell'array
            // serve il blocco do try catch
            do {
                // proviamo a caricare il file dal percorso creato in precedenza
                let data = try Data(contentsOf: URL(fileURLWithPath: filePath))
                // creiamo il decoder
                let decoder = PropertyListDecoder()
                // proviamo a decodificare il file nell'array
                storage = try decoder.decode(Storage.self, from: data)
            } catch {
                // se non ce la fa scriviamo in console l'errore
                debugPrint(error.localizedDescription)
            }

        }
    }

    func salva() {
        objectWillChange.send()
        let encoder = PropertyListEncoder()
        encoder.outputFormat = .xml // impostiamo l'output corretto
        // serve il blocco do try catch
        do {
            // proviamo a codificare l'array
            let data = try encoder.encode(storage)
            // proviamo a salvare l'array codificato nel file
            try data.write(to: URL(fileURLWithPath: filePath))
        } catch {
            // se non ce la fa scriviamo in console l'errore
            debugPrint(error.localizedDescription)
        }
    }

    func newData (nomeApt: String, metar: String, taf: String) {
        let newadd = DataModel(airportName: nomeApt, metar: metar, taf: taf)
        objectWillChange.send()
        storage.append(newadd)
        objectWillChange.send()
        salva()
    }

    func cartellaDocuments() -> String {
        let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
        //print(paths[0])
        return paths[0]
    }
}

as you can see Datamanager is ObservableObject. and I have a simple function (newData) to add the value to the array storage.

now my problem is: if I add data via 3 textfield in the ContentView() my List update automatically with no issue at all

import SwiftUI

struct ContentView: View {
    @ObservedObject var dm : DataManager
    @State var isAddPresented : Bool = false
    @State var nomeApt : String = ""
    @State var metar : String = ""
    @State var taf : String = ""

    var body: some View {

        VStack {
            Button(action: {
                           self.dm.newData(nomeApt: self.nomeApt, metar: self.metar, taf: self.taf)

                       }) {
                           Text("Add from below")
                       }
            TextField("name apt", text: $nomeApt)
            TextField("name apt", text: $metar)
            TextField("name apt", text: $taf)

            Button(action: {
                self.isAddPresented = true
            }) {
                Text("open view to add")
            }.sheet(isPresented: $isAddPresented) {
                Add(dm: DataManager(), dismissFlag: self.$isAddPresented)
            }
            List(dm.storage) { item in
                HStack {
                    Text(item.airportName)
                    Text(item.metar)
                    Text(item.taf)
                }
            }

        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(dm: DataManager())
    }
}

but if I use a sheet to add the same data, using the same function the List not update immediately!

here my second view:

import SwiftUI

struct Add: View {
   @ObservedObject var dm : DataManager
    @State var nomeApt : String = ""
    @State var metar : String = ""
    @State var taf : String = ""
    @Binding var dismissFlag: Bool

    var body: some View {
        VStack {
            Spacer()
            TextField("name apt", text: $nomeApt)
            TextField("name apt", text: $metar)
            TextField("name apt", text: $taf)
            Button(action: {
                self.dm.newData(nomeApt: self.nomeApt, metar: self.metar, taf: self.taf)
                self.dismissFlag = false
            }) {
                Text("Aggiungi")
            }
            Spacer()
        }
    }
}

struct Add_Previews: PreviewProvider {
    static var previews: some View {
        Add(dm: DataManager(), dismissFlag: bindBool())
    }
}

func bindBool() -> Binding<Bool> {
    var boolVariable : Bool = true
    let boolVariableBinding : Binding<Bool> = Binding(get: { boolVariable },
                                                      set: { boolVariable = $0 })
    return boolVariableBinding
}

what I totally don't understand is, why if I load the data from the content view my list update automatically, but if I use a navigation view of a sheet view this not work.

thanks in advance for the help

I have a similar working solution for this.

In your case I would modify your DataModel as follows:

class DataModel: Codable, Identifiable, ObservableObject {
    var id: UUID = UUID()
    @Published var airportName : String = ""
    @Published var metar : String = ""
    @Published var taf : String = ""

    // ...
}

Then in your DataManager :

class DataManager: ObservableObject {

    @Published var storage : [DataModel] = [] // No longer need forobjectWillChange.send()

    // ...
}

Then just use it in your View :

struct ContentView: View {
    @ObservedObject var dm : DataManager
    // ...

    var body: some View {

        VStack {
            // ...
            List(dm.storage) { item in
                // ...
            }

        }
    }
}

The problem was that a lot had changed since the WWDC videos.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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