【问题标题】:How to show a loading animation during firebase query in SwiftUI如何在 SwiftUI 中的 firebase 查询期间显示加载动画
【发布时间】:2020-12-23 02:09:31
【问题描述】:

我正在使用 SwiftUI 构建一个应用程序,并且有一个 ObservableObject 用于查询我的 Firestore 数据库。我的文档比较大,经常需要查询很多,所以我想在查询下载数据的时候加入某种加载指示器。

这是我创建的 ObservableObject 的一个示例:

import FirebaseFirestore
import SwiftUI

struct Document: Identifiable, Equatable {
    var id: String
    var content: String
}

class Fetch: ObservableObject {
    init(loading: Binding<Bool>) {
        self._loading = loading
        readDocuments()
    }
    
    @Published var documents: [Document] = []
    @Binding var loading: Bool
    
    var collection: CollectionReference = Firestore.firestore().collection("DOCUMENTS")
    
    func newDocument(content: String) {
        let id = self.collection.document().documentID
        self.collection.document(id).setData(["id": id, "content": content]) { (error) in handleError(error: error, message: "\(id) CREATED") }
    }
    
    func deleteDocument(document: Document) {
        if self.documents.contains(document) {
            self.collection.document(document.id).delete() { (error) in handleError(error: error, message: "\(document.id) DELETED") }
        } else { print("\(document.id) NOT FOUND") }
    }
    
    func updateDocument(document: Document, update: [String : Any]) {
        if self.documents.contains(document) {
            self.collection.document(document.id).updateData(update) { (error) in handleError(error: error, message: "\(document.id) UPDATED") }
        } else { print("\(document.id) NOT FOUND") }
    }
    
    func readDocuments() {
        self.collection.addSnapshotListener { (snapshot, error) in
            handleError(error: error, message: "READ DOCUMENTS")
            snapshot?.documentChanges.forEach({ (change) in
                if change.type == .added {
                    self.loading = true
                    self.documents.append(Document(id: change.document.get("id") as? String ?? "FAILED TO READ",
                                                   content: change.document.get("content") as? String ?? "FAILED TO READ"))
                    self.loading = false
                }
                if change.type == .modified {
                    self.loading = true
                    self.documents = self.documents.map { (document) -> Document in
                        if document.id == change.document.documentID {
                            let modifiedDocument = Document(id: change.document.get("id") as? String ?? "FAILED TO READ",
                                                 content: change.document.get("content") as? String ?? "FAILED TO READ")
                            return modifiedDocument
                        } else {
                            return document
                        }
                    }
                    self.loading = false
                }
                if change.type == .removed {
                    self.loading = true
                    self.documents.removeAll(where: { $0.id == change.document.documentID })
                    self.loading = false
                }
                
            })
        }
    }
    
}

func handleError(error: Error?, message: String) {
    if error != nil { print((error?.localizedDescription)!); return } else { print(message) }
}

这是我创建的示例视图:

struct ContentView: View {
    @State var loading: Bool = false
    var body: some View {
        NavigationView {
            if loading {
                Color.blue.overlay(Text("Loading View"))
            } else {
                Subview(fetch: Fetch(loading: self.$loading))
            }
        }
    }
}

struct Subview: View {
    @ObservedObject var fetch: Fetch
    @State var newDocumentContent: String = ""
    
    var body: some View {
        VStack(spacing: 0.0) {
            List {
                ForEach(self.fetch.documents) { document in
                    NavigationLink(destination:
                    UpdateDocument(fetch: self.fetch, documentID: document.id)) {
                        Text(document.content)
                    }
                }.onDelete { indexSet in
                    self.deleteDocument(indexSet: indexSet)
                }
            }
            
            Divider()
            
            NewDocument(fetch: self.fetch, newDocumentContent: self.$newDocumentContent)
        }.navigationBarTitle("CRUD", displayMode: .inline)
    }
    
    func deleteDocument(indexSet: IndexSet) {
        self.fetch.deleteDocument(document: self.fetch.documents[indexSet.first!])
    }
}

请记住,对于这个示例,数据并没有大到需要加载视图,这几乎是即时的,但是对于我的应用程序,此代码被分成不同的文件和场景,所以我想我会创建这个例子。

我尝试添加一个绑定布尔值并在加载 readData() 函数时切换它,但 SwiftUI 获取地图并出现错误。

'在视图更新期间修改状态,这将导致未定义的行为。'

【问题讨论】:

    标签: google-cloud-firestore swiftui


    【解决方案1】:

    您需要在@ObservableObject 中使用@Published(而不是@Binding)。

    这是一个可能的演示:

    class Fetch: ObservableObject {
        @Published var loading = false
    
        func longTask() {
            self.loading = true
            // simulates a long asynchronous task (eg. fetching data)
            DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                self.loading = false
            }
        }
    }
    
    struct ContentView: View {
        @ObservedObject private var fetch = Fetch()
    
        var body: some View {
            ZStack {
                Text("Main view")
                if fetch.loading {
                    LoadingView() // can be replaced with any other loading indicator/view
                }
            }
            .onAppear {
                self.fetch.longTask()
            }
        }
    }
    
    struct LoadingView: View {
        var body: some View {
            ZStack {
                Color.black
                    .opacity(0.5)
                    .edgesIgnoringSafeArea(.all)
                Text("Loading")
            }
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多