簡體   English   中英

SwiftUI 視圖未更新為 EnvironmentObject 更改

[英]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/

認為您將ObservableObjectBindableObject 可以試試這個:

@Published var session: User?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM