[英]SwiftUI view is not updating to EnvironmentObject change
我正在創建一個 SwiftUI 應用程序,其中包括 Firebase 以啟用登錄帳戶,非常簡單,只需一個帶有密碼和 email 字段的 ui 表單,然后是提交按鈕。 用戶登錄后,我將 firebase 用戶 object 存儲在 EnvironmentObject 中,以便視圖的 rest 可以訪問它。 該應用程序當前的問題是,一旦用戶登錄並將用戶數據存儲在 EnvironmentObject 中,視圖應該更新為更改后的 state 以顯示不同的屏幕,但似乎視圖仍然認為 EnvironmentObject等於零。 視圖是否不會像 state 變量那樣自動更改為 EnvironmentObject 中的更新?
我確保 EnvironmentObject 設置正確並傳遞給預覽和 SceneDelegate
通過在登錄時將帳戶信息打印到控制台來確保應用程序確實成功登錄了用戶,但視圖本身只會顯示帳戶信息的 nil,它似乎不會使用用戶信息訪問更新的 EnvironmentObject。
import SwiftUI
import Firebase
import Combine
struct ContentView: View {
@EnvironmentObject var session: SessionStore
@State var emailTextField: String = ""
@State var passwordTextField: String = ""
@State var loading = false
@State var error = false
var body: some View {
VStack {
if (session.session != nil) {
Home()
} else {
Form {
TextField("Email", text: $emailTextField)
SecureField("Password", text: $passwordTextField)
Button(action: signIn) {
Text("Sign in")
}
}
Text("Session: \(session.session?.email ?? "no user")")
}
}.onAppear(perform: getUser)
}
func getUser () {
session.listen()
}
func signIn () {
loading = true
error = false
session.signIn(email: emailTextField, password: passwordTextField) { (result, error) in
self.loading = false
if error != nil {
self.error = true
} else {
self.emailTextField = ""
self.passwordTextField = ""
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(SessionStore())
}
}
class SessionStore : ObservableObject {
var didChange = PassthroughSubject<SessionStore, Never>()
var session: User? { didSet { self.didChange.send(self) }}
var handle: AuthStateDidChangeListenerHandle?
func listen () {
// monitor authentication changes using firebase
handle = Auth.auth().addStateDidChangeListener { (auth, user) in
if let account = user {
// if we have a user, create a new user model
print("Got user: \(account)")
self.session = User(
uid: account.uid,
displayName: account.displayName,
email: account.email
)
print("Session: \(self.session?.email ?? "no user")")
} else {
// if we don't have a user, set our session to nil
self.session = nil
}
}
}
func signUp(
email: String,
password: String,
handler: @escaping AuthDataResultCallback
) {
Auth.auth().createUser(withEmail: email, password: password, completion: handler)
}
func signIn(
email: String,
password: String,
handler: @escaping AuthDataResultCallback
) {
Auth.auth().signIn(withEmail: email, password: password, completion: handler)
}
func signOut () -> Bool {
do {
try Auth.auth().signOut()
self.session = nil
return true
} catch {
return false
}
}
func unbind () {
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
}
}
class User {
var uid: String
var email: String?
var displayName: String?
init(uid: String, displayName: String?, email: String?) {
self.uid = uid
self.email = email
self.displayName = displayName
}
}
正如您在視圖中看到的,它應該在用戶未登錄時呈現登錄字段,而當用戶登錄時,視圖應該顯示另一個視圖。 沒有顯示其他視圖。
嘗試使用 @Published 屬性。 嘗試實現這樣的事情:
class SessionStore : ObservableObject {
@Published var session: User
}
class User: ObservableObject {
@Published var uid: String
@Published var email: String?
@Published var displayName: String?
init(uid: String, displayName: String?, email: String?) {
self.uid = uid
self.email = email
self.displayName = displayName
}
}
當用戶 object 發生更改時,這應該會更新您的視圖,例如 email 或顯示名稱,因為它們已發布。 希望這會有所幫助,gl
更新:
因為 SwiftUI 還不支持嵌套的 Observables,所以你需要自己通知你的主 model。
請參閱此片段如何在 ObservableObject 中使用嵌套的 ObservableObject:
class Submodel1: ObservableObject {
@Published var count = 0
}
class Submodel2: ObservableObject {
@Published var count = 0
}
class Model: ObservableObject {
@Published var submodel1: Submodel1 = Submodel1()
@Published var submodel2: Submodel2 = Submodel2()
var anyCancellable: AnyCancellable? = nil
var anyCancellable2: AnyCancellable? = nil
init() {
anyCancellable = submodel1.objectWillChange.sink { (_) in
self.objectWillChange.send()
}
anyCancellable2 = submodel2.objectWillChange.sink { (_) in
self.objectWillChange.send()
}
}
}
當子模型內的數據發生變化時,主 Model 將通知自己。 這將導致視圖更新。
讓我知道這是否對您有所幫助.. 祝你好運!
在實踐中,這個先前的解決方案是可行的,但它是一個非常簡單且不切實際的案例場景。 當您想在非常深的視圖中執行選項卡切換時會發生什么?
通過使用 EnvironmentKey,您可以從任何視圖中獲取當前選項卡狀態並從那里更改它。 我不會寫一個例子,因為這里解釋得很好: https://www.thirdrocktechkno.com/blog/using-swiftui-environments-values-to-change-tab/
認為您將ObservableObject
與BindableObject
? 可以試試這個:
@Published var session: User?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.