You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI中NavigationLink跳转后立即返回的问题求助

SwiftUI + Firebase 注册后跳转自动返回问题

我用SwiftUI搭配Firebase做后端开发应用,想要实现:点击Sign Up按钮后,把用户填写的信息存到Firebase数据库,然后跳转到ProfilePhotoSelecterView并停留在该页面。但实际测试时,点击按钮后虽然能跳转到照片选择页面,但立刻自动返回注册页面。

我用了带destination、isActive和label参数的NavigationLink,通过@Published修饰的布尔属性didAuthenciateUser控制激活状态。以下是相关代码:


@main 视图代码

import SwiftUI
import Firebase

@main
struct Twitter_Demo_YoutubeApp: App {
    
    @StateObject var viewModel = AuthViewModel()
    
    init() {
        FirebaseApp.configure()
    }
    var body: some Scene {
        WindowGroup {
            NavigationView {
                ContentView()
            }
            .environmentObject(viewModel)
        }
    }
}

ContentView 代码

import SwiftUI

struct ContentView: View {
    
    @State private var showManu = false
    @EnvironmentObject var viewModel: AuthViewModel
    
    var body: some View {
        Group {
            // 未登录状态
            if viewModel.userSession == nil {
                LoginView()
                
            } else {
                // 已登录状态
                mainInterfaceView
            }
        }
    }
}

#Preview {
    ContentView()
}

extension ContentView {
    
    var mainInterfaceView: some View {
        ZStack(alignment: .topLeading) {
            MainTabView()
                .navigationBarHidden(showManu)
            
            if showManu {
                ZStack {
                    Color(.black)
                        .opacity(showManu ? 0.25: 0.0)
                }.onTapGesture {
                    withAnimation(.easeInOut) {
                        showManu = false
                    }
                }
                .ignoresSafeArea()
            }
            
            SideManuView()
                .frame(width: 300)
                .offset(x: showManu ? 0 : -300, y: 0)
                .background(showManu ? Color.white : Color.clear)
            
        }
        .navigationTitle("Home")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .navigationBarLeading) {
                
                Button {
                    withAnimation(.easeInOut) {
                        showManu.toggle()
                    }
                } label: {
                    Circle()
                        .frame(width: 32, height: 32)
                }
            }
        }
        .onAppear {
            showManu = false
        }
    }
}

AuthViewModel 代码

class AuthViewModel: ObservableObject {
    
    @Published var userSession: FirebaseAuth.User?
    @Published var didAuthenciateUser = false
    
    init() {
        self.userSession = Auth.auth().currentUser        
    }
func register(withEmail email: String, password: String, fullname: String, username: String) {
        
        Auth.auth().createUser(withEmail: email, password: password) { result, error in
            if let error = error {
                return
            }
            guard let user = result?.user else { return }
                      
            let data = [
                "email": email,
                "username": username.lowercased(),
                "fullname": fullname,
                "uid": user.uid]
            
            Firestore.firestore().collection("users")
                .document(user.uid)
                .setData(data) { _ in
                    self.didAuthenciateUser = true
            }
        }
    }
}

RegistrationView 代码

import SwiftUI

struct RegistrationView: View {
    @State private var email = ""
    @State private var username = ""
    @State private var fullname = ""
    @State private var password = ""
    @Environment(\.presentationMode) var presentationMode
    @EnvironmentObject var viewModel: AuthViewModel
    
    var body: some View {
        VStack {
            NavigationLink(destination: ProfilePhotoSelecterView(),
                           isActive: $viewModel.didAuthenciateUser, 
                           label: { }) // 控制跳转的NavigationLink
            
            AuthHeaderView(title1: "Get started.", title2: "Create your account")
            
            VStack(spacing: 40) {
                CustomInputField(imageName: "envelope",
                                 placeholderText: "Email",
                                 text: $email)
                 
                 CustomInputField(imageName: "person",
                                  placeholderText: "username",
                                  text: $username)
                
                CustomInputField(imageName: "person",
                                 placeholderText: "Full Name",
                                 text: $fullname)
                 
                 CustomInputField(imageName: "lock",
                                  placeholderText: "Password",
                                  isSecureField: true,
                                  text: $password)
                
            }
            .padding(32)
            
            Button {
                viewModel.register(withEmail: email,
                                   password: password,
                                   fullname: fullname,
                                   username: username)
            } label: {
                Text("Sign Up")
                    .font(.headline)
                    .foregroundColor(.white)
                    .frame(width: 340, height: 50)
                    .background(Color(.systemBlue))
                    .clipShape(Capsule())
                    .padding()
            }
            .shadow(color: .gray.opacity(0.5), radius: 10, x: 0, y: 0)
            
            
            Spacer()
            
            Button {
                presentationMode.wrappedValue.dismiss()
            } label: {
                HStack {
                    Text("Already have account?")
                        .font(.footnote)
                    Text("Sign In")
                        .font(.footnote)
                        .fontWeight(.semibold)
                }
            }
            .padding(.bottom, 32)
        }
        .ignoresSafeArea()
    }
}

#Preview {
    RegistrationView()
}

ProfilePhotoSelecterView 代码

import SwiftUI

struct ProfilePhotoSelecterView: View {
    
    var body: some View {
        
        VStack {
            AuthHeaderView(title1: "Create your account.", 
                           title2: "Add a profile photo")
            
            Button {
               print("Pick image here..")
            } label: {
                Image("addphoto")
                    .resizable()
                    .scaledToFill()
                    .frame(width: 180, height: 180)
                    .padding(.top, 44)
            }
            
            Spacer()
        }
        .ignoresSafeArea()
    }
}

#Preview {
    ProfilePhotoSelecterView()
}

相关截图说明

  • 初始页面:显示注册输入界面,包含邮箱、用户名、全名、密码输入框和Sign Up按钮
  • 点击Sign Up按钮后的异常页面:短暂显示的照片选择页面,提示添加头像
  • 最终返回页面:回到初始的注册输入界面

问题原因

注册成功后,你既没有及时更新userSession,也没有处理导航状态的重置逻辑:

  1. 注册成功后userSession仍为nil,ContentView会持续显示登录/注册流程的视图,当导航栈切换时会销毁ProfilePhotoSelecterView
  2. didAuthenciateUser状态在跳转后没有重置,会导致导航逻辑混乱

解决步骤

1. 修改AuthViewModel,拆分注册与登录流程

新增临时变量存储刚注册的用户,等头像设置完成后再正式激活用户会话:

class AuthViewModel: ObservableObject {
    
    @Published var userSession: FirebaseAuth.User?
    @Published var didAuthenciateUser = false
    private var tempRegisteredUser: FirebaseAuth.User? // 临时存储刚注册的用户
    
    init() {
        self.userSession = Auth.auth().currentUser
    }
    
    func register(withEmail email: String, password: String, fullname: String, username: String) {
        Auth.auth().createUser(withEmail: email, password: password) { result, error in
            guard error == nil, let user = result?.user else {
                print(error?.localizedDescription ?? "注册失败")
                return
            }
            
            self.tempRegisteredUser = user
            
            let userData = [
                "email": email,
                "username": username.lowercased(),
                "fullname": fullname,
                "uid": user.uid,
                "profileImageUrl": ""
            ]
            
            Firestore.firestore().collection("users").document(user.uid).setData(userData) { _ in
                self.didAuthenciateUser = true // 触发跳转到头像选择页
            }
        }
    }
    
    // 完成头像设置后正式登录
    func completeProfile(withImageUrl imageUrl: String) {
        guard let user = tempRegisteredUser else { return }
        
        Firestore.firestore().collection("users").document(user.uid).updateData([
            "profileImageUrl": imageUrl
        ]) { _ in
            self.userSession = user
            self.didAuthenciateUser = false
            self.tempRegisteredUser = nil
        }
    }
}

2. 调整ProfilePhotoSelecterView,处理状态重置与头像设置逻辑

添加环境对象绑定,跳转后重置导航状态,并预留头像上传完成后的回调:

import SwiftUI

struct ProfilePhotoSelecterView: View {
    @EnvironmentObject var viewModel: AuthViewModel
    
    var body: some View {
        VStack {
            AuthHeaderView(title1: "Create your account.", 
                           title2: "Add a profile photo")
            
            Button {
                // 替换为实际图片选择逻辑,示例:
                // viewModel.completeProfile(withImageUrl: "你的头像URL")
                print("Pick image here..")
            } label: {
                Image("addphoto")
                    .resizable()
                    .scaledToFill()
                    .frame(width: 180, height: 180)
                    .padding(.top, 44)
            }
            
            Spacer()
        }
        .ignoresSafeArea()
        .onAppear {
            viewModel.didAuthenciateUser = false // 重置状态避免重复跳转
        }
    }
}

#Preview {
    ProfilePhotoSelecterView()
}

3. 保持ContentView原有逻辑

无需修改ContentView的判断逻辑,因为注册流程未完成时userSession始终为nil,LoginView的导航栈会完整保留,不会打断跳转后的ProfilePhotoSelecterView。


内容的提问来源于stack exchange,提问作者user21666271

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 07:18:13