简体   繁体   English

从子导航视图中关闭选项卡视图并返回到 RootView SwiftUI

[英]Dismiss Tab View from a Child Navigation View and go back to RootView SwiftUI

My Navigation Flow:我的导航流程:

在此处输入图片说明

Here, my View A to View G is under one Navigation View.在这里,我的View AView G位于一个导航视图下。

NavigationView {
    ViewA()
}

And from View D & View G I am moving to my TabView H by modal, like this:View D & View G我通过模态移动到我的TabView H ,如下所示:

Button(action: {
    isPresented.toggle()
}, label: {
   Text("GO!")
})
.fullScreenCover(isPresented: $isPresented) {
    TabbarView()
}

In my Tab View all the views have their own Navigation View, like this:在我的选项卡视图中,所有视图都有自己的导航视图,如下所示:

TabView(selection: $tabbarViewModel.tabSelection) {
    NavigationView {
        HomeView()
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Home")
                }
            }
    }.navigationViewStyle(StackNavigationViewStyle())
        .tabItem {
            Image(systemName: "house")
                .renderingMode(.template)
                .resizable()
                .aspectRatio(contentMode: .fit)
            Text("Home")
        }
        .tag(0)

    NavigationView {
        CartView()
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Cart")
                }
            }
    }.navigationViewStyle(StackNavigationViewStyle())
        .tabItem {
            Image(systemName: "cart")
                .renderingMode(.template)
                .resizable()
                .aspectRatio(contentMode: .fit)
            Text("Cart")
        }
        .tag(1)

    NavigationView {
        ProductView()
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Product")
                }
            }
    }.navigationViewStyle(StackNavigationViewStyle())
        .tabItem {
            Image(systemName: "seal")
                .renderingMode(.template)
                .resizable()
                .aspectRatio(contentMode: .fit)
            Text("Product")
        }
        .tag(2)

    NavigationView {
        ProfileView()
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .principal) {
                    Text("Profile")
                }
            }
    }.navigationViewStyle(StackNavigationViewStyle())
        .tabItem {
            Image(systemName: "person")
                .renderingMode(.template)
                .resizable()
                .aspectRatio(contentMode: .fit)
            Text("Profile")
        }
        .tag(3)
}
.accentColor(Color("AppsDefaultColor"))

Now I want to go back to viewA , say from Home View by pressing the Sign Out button.现在我想返回到viewA ,比如通过按Sign Out按钮从Home View I tried this, just to see if it takes me back to previous view, but it doesn't work.我试过这个,只是想看看它是否能让我回到以前的视图,但它不起作用。

struct HomeView: View {
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        Button(action: {
            self.presentationMode.wrappedValue.dismiss()
        }, label: {
            Text("Dismiss")
        })
    }
}

So how can I dismiss the tabview and go back to my Root view A ?那么如何关闭 tabview 并返回到我的Root view A呢?

Finally I have managed to achieve this.最后,我设法实现了这一目标。 To roll back to the Root view I used this:为了回滚到根视图,我使用了这个:

NavigationLink(destination: <#T##_#>, tag: <#T##Hashable#>, selection: <#T##Binding<Hashable?>#>, label: <#T##() -> _#>)

And to dismiss the presented view, I used this:为了消除所呈现的视图,我使用了这个:

UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true, completion: nil)

To be honest it's quite simple.说实话,这很简单。 All we need to make NavigationLink selection which is selectedItem in this case to nil & dismiss the modal throughout the project.我们只需要在本例中选择NavigationLink selection整个项目中nildismiss模式。 All of these have done inside of a tab bar view model class with the help of @EnvironmentObject所有这些都在@EnvironmentObject的帮助下在选项卡栏视图模型类中@EnvironmentObject

First create the TabbarViewModel: ObservableObject :首先创建TabbarViewModel: ObservableObject :

import Foundation
import SwiftUI

class TabbarViewModel: ObservableObject {
    @Published var tabSelection: Int = 0
    @Published var selectedItem: String? = nil
    
    func gotoRootView() {
        withAnimation {
            UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true, completion: nil)
            selectedItem = nil
        }
    }
}

Now, let's create the ViewA which is AuthListView :现在,让我们创建ViewAAuthListView

import SwiftUI

struct CellItem: Identifiable {
    var id = UUID()
    let title: String
    let image: String
    let destination: AnyView
}

struct AuthListView: View {
    var body: some View {
        AuthListContentView()
            .navigationBarHidden(true)
    }
}

struct AuthListContentView: View {
    @State private var cellList: [CellItem] = [
        CellItem(title: "Icon", image: "", destination: AnyView(EmptyView())),
        CellItem(title: "Phone", image: "Phone", destination: AnyView(PhoneView())),
        CellItem(title: "Email", image: "Email", destination: AnyView(SignInView())),
        CellItem(title: "Google", image: "Google", destination: AnyView(GoogleView())),
        CellItem(title: "Facebook", image: "Facebook", destination: AnyView(FacebookView())),
        CellItem(title: "Twitter", image: "Twitter", destination: AnyView(TwitterView()))]
    
    var body: some View {
        List(cellList, id: \.id) { item in
            if item.title == "Icon" {
                IconImageView()
            } else {
                AuthListCell(cellItem: item)
            }
        }
    }
}

struct IconImageView: View {
    var body: some View {
        VStack {
            Image("firebase")
                .renderingMode(.original)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(width: 120, height: 120, alignment: .center)
        }
        .frame(maxWidth: .infinity, minHeight: 120, alignment: .center)
        .padding(.top, 50)
        .padding(.bottom, 50)
    }
}

This is each cell of auth View:这是 auth View 的每个单元格:

import SwiftUI

struct AuthListCell: View {
    var cellItem: CellItem
    @EnvironmentObject var tabbarViewModel: TabbarViewModel
    
    var body: some View {
        
        NavigationLink(
            destination: cellItem.destination,
            tag: cellItem.title,
            selection: $tabbarViewModel.selectedItem) {
            cell(cellItem: cellItem)
        }
    }
}

struct cell: View {
    var cellItem: CellItem
    var body: some View {
        HStack(spacing: 15) {
            Image(cellItem.image)
                .renderingMode(.original)
                .resizable()
                .aspectRatio(contentMode: .fit)
                .frame(maxWidth: 28, maxHeight: .infinity, alignment: .center)

            Text(cellItem.title)
                .foregroundColor(Color("DefaultText"))
                .font(.system(size: 17))
            Spacer()
        }
        .padding(.top, 5)
        .padding(.bottom, 5)
    }
}

Load this view inside of your ContentView under a Navigation View:在导航视图下的ContentView加载此视图:

struct ContentView: View {
    @Environment(\.managedObjectContext) private var viewContext

    var body: some View {
        NavigationView {
            AuthListView()
        }
        .navigationViewStyle(StackNavigationViewStyle())
    }
}

Till now, we can push to ViewB from ViewA .到现在为止,我们可以推到ViewBViewA Here, I am only showing the navigation flow for ViewA push> ViewB push> ViewC present> TabView > and then Dismiss TabView from HomeView and go back to root ViewA , cause rest of the other views will follow the same.在这里,我只显示了导航流程ViewA推> ViewB推> ViewC目前> TabView >,然后辞退TabViewHomeView并返回根ViewA ,其他视图事业其余部分将遵循相同。 It also works from a child navigation of any Tab bar views as well.它也适用于任何标签栏视图的子导航。 So let's create ViewB ( PhoneView ) and push to ViewC ( PINView ):因此,让我们创建ViewBPhoneView )和推ViewCPINView ):

ViewB:视图B:

struct PhoneView: View {
    var body: some View {
        PhoneContentView()
            .navigationBarTitle("Phone Number", displayMode: .inline)
    }
}

struct PhoneContentView: View {
    var body: some View {
        NavigationLink(destination: PINView()) {
            Text("Go")
        }
    }
}

ViewC:视图C:

struct PINView: View {
    var body: some View {
        PINContentView()
            .navigationBarTitle("PIN", displayMode: .inline)
    }
}

struct PINContentView: View {
    @State private var isPresented = false
    
    var body: some View {
        Button(action: {
            isPresented.toggle()
        }, label: {
            Text("Sign In")
        })
        .fullScreenCover(isPresented: $isPresented) {
            TabbarView()
        }
    }
}

Till now we have presented the tab view from previous ViewC .到目前为止,我们已经展示了之前ViewC的选项卡视图。 This is our tab view:这是我们的选项卡视图:

import SwiftUI

struct TabbarView: View {
    @EnvironmentObject var tabbarViewModel: TabbarViewModel

    var body: some View {
        TabView(selection: $tabbarViewModel.tabSelection) {
            NavigationView {
                HomeView().navigationBarTitle("Home", displayMode: .inline)
            }
            .navigationViewStyle(StackNavigationViewStyle())
            .tabItem {
                Image(systemName: "house")
                    .renderingMode(.template)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                Text("Home")
            }
            .tag(0)

            NavigationView {
                CartView().navigationBarTitle("Cart", displayMode: .inline)
            }
            .navigationViewStyle(StackNavigationViewStyle())
            .tabItem {
                Image(systemName: "cart")
                    .renderingMode(.template)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                Text("Cart")
            }
            .tag(1)

            NavigationView {
                ProductView().navigationBarTitle("Product", displayMode: .inline)
            }
            .navigationViewStyle(StackNavigationViewStyle())
            .tabItem {
                Image("product")
                    .renderingMode(.template)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                Text("Product")
            }
            .tag(2)
            
            NavigationView {
                ProfileView().navigationBarTitle("Profile", displayMode: .inline)
            }
            .navigationViewStyle(StackNavigationViewStyle())
            .tabItem {
                Image(systemName: "person")
                    .renderingMode(.template)
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                Text("Profile")
            }
            .tag(3)
        }
        .accentColor(Color("AppsDefaultColor"))
    }
}

Now, If I want to dismiss the presented tab view & go back to root view by pressing sign out button from my HomeView , all I have to do is call tabbarViewModel.gotoRootView() like this:现在,如果我想关闭显示的选项卡视图并通过从我的HomeView按下sign out按钮返回根视图,我所要做的就是像这样调用tabbarViewModel.gotoRootView()

struct HomeView: View {
    @EnvironmentObject var tabbarViewModel: TabbarViewModel
    
    var body: some View {
        Button(action: {
            tabbarViewModel.gotoRootView()
        }, label: {
            Text("Sign Out")
        })
    }
}

I can dismiss the tab view and go to the root view from a child view of my HomeView as well.我可以关闭选项卡视图并从我的 HomeView 的子视图转到根视图。 Let's go to a child view from the HomeView:让我们从 HomeView 转到子视图:

struct HomeView: View {
    
    var body: some View {
        NavigationLink(destination: HomeDetailsView()) {
            Text("Go")
        }
    }
}

This is the HomedetailsView` and by following the same call I can accomplish the same result like this:这是 HomedetailsView`,通过遵循相同的调用,我可以完成相同的结果,如下所示:

struct HomeDetailsView: View {
    var body: some View {
        HomeDetailsContentView()
        .navigationBarTitle("Home Details", displayMode: .inline)
    }
}

struct HomeDetailsContentView: View {
    @EnvironmentObject var tabbarViewModel: TabbarViewModel
    
    var body: some View {
        Button(action: {
            tabbarViewModel.gotoRootView()
        }, label: {
            Text("Dismiss")
        })
    }
}

By this you can dismiss tab view and go to the root view from any view of your project.通过这种方式,您可以关闭选项卡视图并从项目的任何视图转到根视图。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM