如何在SwiftUI中的Firebase查询期间显示加载动画 [英] How to show a loading animation during firebase query in SwiftUI
问题描述
我正在使用SwiftUI构建应用程序,并且具有一个ObservableObject来查询我的Firestore数据库.我的文档比较大,经常需要查询很多文档,因此我想在查询下载数据时合并某种加载指示器.
I am building an app with SwiftUI, and have an ObservableObject for querying my Firestore database. My documents are relatively large and I often need to query a lot of them, so I wanted to incorporate some sort of loading indicator while the query is downloading the data.
这是我创建的ObservableObject的示例:
This is an example of the ObservableObject I have created:
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) }
}
这是我创建的示例视图:
This is the example view I have created:
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!])
}
}
请记住,在此示例中,数据并不需要加载视图那么大,这几乎是即时的,但是对于我的应用程序,此代码被拆分为不同文件和场景的加载,因此我认为我将创建这个例子.
Keep in mind for this example the data is not nearly as big to need a loading view, this is pretty much instant, but for my application this code is split into loads of different files and scenarios so I thought I'd create this example.
我尝试添加一个绑定布尔值,并在readData()函数加载时切换它,但是SwiftUI获取map并出现错误.
I have tried adding a binding boolean and toggling it when the readData() function is loading, but SwiftUI gets map and I get an error.
在视图更新期间修改状态,这将导致未定义的行为."
'Modifying state during view update, this will cause undefined behavior.'
推荐答案
您需要在 @ObservableObject
中使用 @Published
(不是 @Binding 代码>).
You need to use @Published
in the @ObservableObject
(not @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")
}
}
}
这篇关于如何在SwiftUI中的Firebase查询期间显示加载动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!