导航视图中视图之间的突然转换

Posted

技术标签:

【中文标题】导航视图中视图之间的突然转换【英文标题】:Abrupt transition between views within a Navigation View 【发布时间】:2021-09-05 19:57:53 【问题描述】:

我无法弄清楚如何从我的 SignInView() 顺利导航到我的 FirstView()。我的 FirstView() 在 Navigation Stack 中,但是视图之间的转换非常突然,并且没有通常使用 NavigationLink 获得的转换。我怎样才能让过渡工作?

非常感谢!

这里是相关代码...

struct ContentView: View 
    @EnvironmentObject var viewModel: AppViewModel
    
    var body: some View 
        VStack
            NavigationView 
                if viewModel.signedIn 
                    FirstView()
                        .transition(.slide)
                 else 
                    //.onAppear method is used for keyboard management (See Misc Functions...)
                    SignInView()
                        .onAppear(perform: UIApplication.shared.addTapGestureRecognizer)
                        .navigationBarHidden(true)
                
            
            .onAppear 
                viewModel.listen()
            
        
        
    




class AppViewModel: ObservableObject 
    private var db = Firestore.firestore()
    
    @Published var userInfo: User?
    @Published var signedIn: Bool = false
    
    var handle: AuthStateDidChangeListenerHandle?
    let authRef = Auth.auth()
    
    var authHandle : AuthStateDidChangeListenerHandle?
    var rootInfoCollection : CollectionReference!
    var userIdRef = ""

    
    
    func fetchUserData()
        db.collection("Users").document("\(userIdRef)").getDocument  document, error in
            // Check for error
            if error == nil 
                // Check that this document exists
                if document != nil && document!.exists 
                    
                    self.userInfo = document.map  (documentSnapshot) -> User in
                        let data = documentSnapshot.data()
                        
                        let uid = data?["uid"] as? UUID ?? UUID()
                        let company = data?["company"] as? String ?? ""
                        let name = data?["name"] as? String ?? ""
                        let admin = data?["admin"] as? Bool ?? false
                        let photo = data?["photo"] as? String ?? ""
                        
                        return User(uid: uid, company: company, name: name, admin: admin, photo: photo)
                    
                    
                    withAnimation 
                        self.signedIn = true
                    
                    
                    
                
            
        
        
    
    
    func listen()
        handle = authRef.addStateDidChangeListener( auth, user in
            print(user?.email ?? "No User Found")
            
            if let user = auth.currentUser 
                self.userIdRef = user.uid
                self.rootInfoCollection = Firestore.firestore().collection("/Users/")
                
                DispatchQueue.main.async 
                    self.fetchUserData()
                
                                
             else 
                self.signedIn = false
            
        )
        
    
    
    func signIn(email: String, password: String)
        authRef.signIn(withEmail: email, password: password)  result, error in
            guard result != nil, error == nil else 
                return
            
        
    





struct SignInView: View 
    
    @EnvironmentObject var viewModel: AppViewModel
    
    @State private var username : String = ""
    @State private var password : String = ""
    @State private var shouldShowLoginAlert: Bool = false
    @State var selectedImageArray : [Image] = []
    
    var disableLoginButton : Bool 
        return self.username.isEmpty || self.password.isEmpty
    
    
    var body: some View 
        
        VStack
            
            Image(uiImage: #imageLiteral(resourceName: "awText"))
                .resizable()
                .frame(width: 180, height: 100)
                .padding(.bottom, 50)
            
            TextField("Email", text: $username)
                .padding(.leading)
                .disableAutocorrection(true)
                .autocapitalization(.none)
            Rectangle().fill(Color.gray.opacity(0.25)).frame(height: 1, alignment: .center).padding(.bottom)
                .padding(.bottom)
                .onChange(of: self.username, perform:  value in
                    if value.count > 10 
                        self.username = String(value.prefix(20)) //Max 10 Characters for Username.
                    
                )
            
            SecureField("Password", text: $password)
                .padding(.leading)
                .disableAutocorrection(true)
                .autocapitalization(.none)
            Rectangle().fill(Color.gray.opacity(0.25)).frame(height: 1, alignment: .center)
                .onChange(of: self.username, perform:  value in
                    if value.count > 10 
                        self.username = String(value.prefix(10)) //Max 10 Characters for Password.
                    
                )
            
            
            //SignIn Button
            Button(action: 
                viewModel.signIn(email: username, password: password)
            , label: 
                Text("Sign In")
                    .disabled(disableLoginButton)
                    .frame(width: 300, height: 50)
                    .background(Color.green)
                    .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
                    .padding()
            )

【问题讨论】:

您可以尝试使用FirstView().transition(.slide) 吗?你怎么看? 不幸的是这没用.. oups 是的,你需要使用withAnimation,有人在下面发布了答案 【参考方案1】:

用您自己的动画替换默认的NavigationView 行为并不一定很简单。我将列出一种可能性,但另一种可能是使用真正的NavigationView 转换,但是一旦您使用FirstView,只需隐藏后退按钮。

要自己进行转换,您需要 一个NavigationView 的根元素、一个if 子句、一个transition(.slide)withAnimation。这是您的代码的简化版本,仅显示了这些元素:

class AppViewModel: ObservableObject 
    @Published var signedIn = false


struct FirstView : View 
    var body: some View 
        Text("Signed in")
    


struct ContentView: View 
    @StateObject var viewModel = AppViewModel()
    
    var body: some View 
        
        NavigationView 
            VStack 
                if viewModel.signedIn 
                     FirstView()
                        .transition(.slide)
                 else 
                    Button("Sign me in") 
                        withAnimation 
                            viewModel.signedIn = true
                        
                    
                
            
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .navigationBarHidden(true)
        
    

【讨论】:

不幸的是,这也不起作用:( @RapsIn4 如果您将我提供的代码复制并粘贴到 Xcode 中,它就可以工作。你能确定你的实施有什么不同吗? 我更新了上面的代码以反映您的建议。除了使用 Firebase 之外,我看不出代码有任何差异。 有很大的不同——看VStackNavigationView的顺序 哈哈,就是这样!不敢相信我错过了。我真的很感谢你帮我解决这个问题。谢谢!

以上是关于导航视图中视图之间的突然转换的主要内容,如果未能解决你的问题,请参考以下文章

导航时如何在 SwiftUI 中使用自定义视图转换?

UIViewController 导航 - 在意外状态下完成导航转换。导航栏子视图树可能会损坏

如何在导航视图控制器上滑动时进行即时转换

在导航堆栈中以模态方式呈现视图控制器

在父表视图的同级详细视图之间导航

添加子视图会在子视图和导航栏之间留下空隙