SwiftUI中onReceive无法触发NavigationLink实现Firebase登录后导航
SwiftUI + Firebase登录后无法导航到主页的解决方案
问题出在你用onReceive回调创建NavigationLink的方式——SwiftUI的视图必须存在于视图树中才能生效,回调里临时创建的NavigationLink不会被渲染,所以导航触发不了,这也是为什么控制台能打印但页面没跳转的原因。
修复步骤:
调整ViewModel的登录状态赋值:
把self.isSignedIn.toggle()改成直接赋值true,避免重复登录时状态反转的问题(比如用户退出后再次登录,toggle会把已登录的true又改成false)。在视图树中添加受控的NavigationLink:
将NavigationLink放在NavigationView内部的视图层级里,通过绑定viewModel.isSignedIn来控制导航激活状态,并且隐藏这个链接(因为我们不需要显示点击入口,只是用它触发自动导航)。
修改后的代码:
LoginViewModel
class LoginViewModel: ObservableObject { let auth = Auth.auth() var userInfo: User? @Published var isSignedIn = false func signIn(email: String, password: String) { auth.signIn(withEmail: email, password: password) { _, error in if let error = error as? NSError { print("Error happened on login"+error.description) } else { print("Login successful") if let user = self.auth.currentUser { print("We have a user") self.userInfo = user // 直接赋值为true,替代toggle避免状态异常 self.isSignedIn = true } } } } }
LoginPage
struct LoginPage: View { @State var email = "" @State var password = "" @ObservedObject var viewModel = LoginViewModel() var body: some View { NavigationView { ZStack { VStack { TextField("Email", text: $email).padding() .background(Color(.secondarySystemBackground)) .padding() TextField("Password", text: $password).padding() .background(Color(.secondarySystemBackground)) .padding() Button(action: { guard !email.isEmpty, !password.isEmpty else { return } viewModel.signIn(email: email, password: password) }, label: { Text("Sign in") .padding(EdgeInsets(top: 12, leading: 35, bottom: 12, trailing: 35)) .foregroundColor(.white) .background(Color.blue) .cornerRadius(15) }) } // 添加受控的NavigationLink,隐藏在视图中 NavigationLink( destination: HomePage(), isActive: $viewModel.isSignedIn, label: { EmptyView() } ) .hidden() }.navigationTitle("Login") } // 保留onReceive做打印或其他逻辑(可选) .onReceive(viewModel.$isSignedIn) { isSignedIn in if isSignedIn { print("ok damm") } } } }
额外说明:
- 如果你需要在用户退出时返回登录页,只需要在
HomePage里将viewModel.isSignedIn设为false即可,这个绑定会自动触发导航返回。 - 建议用
@StateObject替代@ObservedObject来初始化LoginViewModel(iOS 14+),确保ViewModel的生命周期和视图一致:@StateObject var viewModel = LoginViewModel()
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

