【问题标题】:How do I update a view after retrieving data from Firestore?从 Firestore 检索数据后如何更新视图?
【发布时间】:2021-12-31 06:03:47
【问题描述】:

我有一个 iOS 小部件,我想在从 Firestore 检索数据后更新小部件视图。我浏览了整个互联网,包括this question。检索数据后,我无法更新视图。请查看以下代码以获取小部件的视图。数据已检索,但我无法在检索数据后更新 UI 元素。

struct Runner_WidgetEntryView: View {
    @State var text = ""
    @State var textLoaded = false
    var entry: Provider.Entry
    
    var body: some View {
        ZStack {
            Image("back").resizable().scaledToFit()
            Rectangle().foregroundColor(.black).cornerRadius(10).padding([.leading, .trailing], 10).padding([.top, .bottom], 15).overlay(Text(textLoaded ? text : "Loading..."))
        }.onAppear {
            let dispatchGroup = DispatchGroup()
            FirebaseApp.configure()
            let db = Firestore.firestore()
            dispatchGroup.enter()
            db.collection("collection").getDocuments { (snapshot, error) in
                if error != nil {
                    text = "There was an error retrieving the text."
                    dispatchGroup.leave()
                }
                if let snapshot = snapshot {
                    for document in snapshot.documents {
                        if document.documentID == "Document ID" {
                            text = document.data()["qoute_name"] as! String
                            dispatchGroup.leave()
                        }
                    }
                }
                else {
                    text = "There was an error retrieving the text."
                    dispatchGroup.leave()
                }
            }
            dispatchGroup.notify(queue: .main) {
                textLoaded = true
            }
        }
    }
}

检索数据后如何更新TextView

【问题讨论】:

  • 我在您的代码中没有看到TextView。表明这一点似乎很重要。
  • 文本视图在矩形的 .overlay 内。

标签: ios swiftui


【解决方案1】:

SwiftUI 需要在主线程上更新其变量。 因此,如果您没有看到更新,可能是由于涉及的线程不同。 试试这个来更新主线程上的text

EDIT-1:

.onAppear {
    var fireTxt = ""  // <-- here
    let dispatchGroup = DispatchGroup()
    FirebaseApp.configure()
    let db = Firestore.firestore()
    dispatchGroup.enter()
    
    db.collection("collection").getDocuments { (snapshot, error) in
            if error != nil {
                fireTxt = "There was an error retrieving the text."  // <-- here
            }
            if let snapshot = snapshot {
                fireTxt = "No document found"  // <-- here default text
                for document in snapshot.documents {
                    if document.documentID == "Document ID" {
                        fireTxt = document.data()["qoute_name"] as! String   // <-- here
                    }
                }
            }
            else {
                fireTxt = "There was an error retrieving the text."  // <-- here
            }
            dispatchGroup.leave()
        }
    
    dispatchGroup.notify(queue: .main) {
        textLoaded = true
        text = fireTxt    // <-- here on the main thread
    }
}

注意,在.onAppear 中进行所有这些数据库获取并不是一个好主意。您应该为此使用视图模型。

【讨论】:

  • 感谢您的回答。我试过了,但 TextView 永远不会从“正在加载...”中改变。如何使错误消息或文本显示?
  • 嗯,在我的测试中有效。确保 Rectangle().foregroundColor(...) 是您可以看到的颜色,例如:.foregroundColor(.green) 作为测试。
  • 我可以看到“正在加载...”,所以这不是前景色的问题。状态未更新,因此不会更改为检索到的文本。
  • 我发现有些路径在您的逻辑中不适用。尝试在 for document in snapshot.documents {...} 循环之前添加 if snapshot.documents.isEmpty { fireTxt = "No documents in db"; dispatchGroup.leave() }。此外,如果if document.documentID == "Document ID" {...} 中没有文档,则不会有短信,也不会有dispatchGroup.leave()
  • 我用新的if let snapshot = snapshot {...}更新了我的答案
【解决方案2】:

在建立@workingdog 的答案和大量测试之后,我想出了这个解决方案:

SwiftUI 状态无法从闭包中更新,因此无法从 dispatchGroup.notify 或任何其他闭包中更新。

解决方法是在创建时间线时从 Firebase 检索数据,然后将检索到的数据保存到 UserDefaults。从那里,可以在 SwiftUI 视图的onAppear 方法中从UserDefaults 读取数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    • 1970-01-01
    • 2021-05-29
    • 2020-11-21
    • 1970-01-01
    • 2015-12-17
    相关资源
    最近更新 更多