首页 > 解决方案 > ObservedObject 列表更新

问题描述

我正在尝试学习 SwiftUI,我尝试在插入值后自动更新我的列表。但我遇到了一个大问题!当我使用工作表或导航视图插入数据时,我的列表不会更新,它仅在我从我的 contentView 加载数据时才有效。

(而且我不明白为什么,DataManager 类是 ObservableObject 并且如果我在内容视图中加载带有 3 个文本字段的数据,则它可以完美地工作)

在我的项目下面:我有一个数据模型


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

我有一个数据管理器

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

如您所见,Datamanager 是 ObservableObject!我有一个简单的函数(newData)来将值添加到数组存储中。

现在我的问题是:如果我通过 ContentView() 中的 3 个文本字段添加数据,我的列表会自动更新,完全没有问题

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

但是如果我使用工作表添加相同的数据,使用相同的功能列表不会立即更新!

这是我的第二个观点:

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
}

我完全不明白的是,为什么如果我从内容视图加载数据,我的列表会自动更新,但如果我使用工作表视图的导航视图,这不起作用。

在此先感谢您的帮助

标签: arraysswiftxcodeobservablecombine

解决方案


我有一个类似的工作解决方案。

在你的情况下,我会修改你DataModel如下:

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

    // ...
}

然后在你的DataManager

class DataManager: ObservableObject {

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

    // ...
}

然后只需在您的View

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

    var body: some View {

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

        }
    }
}

问题是自从 WWDC 视频以来发生了很多变化。


推荐阅读