从子导航视图中关闭选项卡视图并返回到 RootView SwiftUI
Dismiss Tab View from a Child Navigation View and go back to RootView SwiftUI
我的导航流程:
在这里,我的 View A
到 View G
在一个导航视图下。
NavigationView {
ViewA()
}
然后从 View D
和 View G
我正在通过模态移动到我的 TabView H
,像这样:
Button(action: {
isPresented.toggle()
}, label: {
Text("GO!")
})
.fullScreenCover(isPresented: $isPresented) {
TabbarView()
}
在我的选项卡视图中,所有视图都有自己的导航视图,如下所示:
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"))
现在我想返回 viewA
,比如从 Home View
按 Sign Out
按钮。我试过这个,只是想看看它是否让我回到之前的视图,但它不起作用。
struct HomeView: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Dismiss")
})
}
}
那么我怎样才能关闭 tabview 并返回到我的 Root view A
?
我终于成功了。要回滚到 Root 视图,我使用了这个:
NavigationLink(destination: <#T##_#>, tag: <#T##Hashable#>, selection: <#T##Binding<Hashable?>#>, label: <#T##() -> _#>)
为了关闭呈现的视图,我使用了这个:
UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true, completion: nil)
老实说,这很简单。 我们需要做的就是 NavigationLink selection
,在这种情况下是selectedItem nil
& dismiss
整个项目的模式。 所有这些都是在 @EnvironmentObject
的帮助下在标签栏视图模型中完成的 class
首先创建 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
}
}
}
现在,让我们创建 ViewA
,即 AuthListView
:
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)
}
}
这是 auth 视图的每个单元格:
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)
}
}
在导航视图下的 ContentView
中加载此视图:
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
var body: some View {
NavigationView {
AuthListView()
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
到目前为止,我们可以从 ViewA
推送到 ViewB
。在这里,我只展示了 ViewA
push> ViewB
push> ViewC
present> TabView
> 的导航流程,然后 Dismiss TabView
from HomeView
并返回根 ViewA
,因为其他视图的其余部分将遵循相同的。它也适用于任何选项卡栏视图的子导航。所以让我们创建 ViewB
(PhoneView
) 并推送到 ViewC
(PINView
):
视图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")
}
}
}
视图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()
}
}
}
到目前为止,我们已经展示了之前 ViewC
中的选项卡视图。这是我们的选项卡视图:
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"))
}
}
现在,如果我想关闭显示的选项卡视图并通过按 HomeView
中的 sign out
按钮返回根视图,我所要做的就是调用 tabbarViewModel.gotoRootView()
像这样:
struct HomeView: View {
@EnvironmentObject var tabbarViewModel: TabbarViewModel
var body: some View {
Button(action: {
tabbarViewModel.gotoRootView()
}, label: {
Text("Sign Out")
})
}
}
我也可以关闭选项卡视图并从我的 HomeView 的子视图转到根视图。让我们从 HomeView 转到子视图:
struct HomeView: View {
var body: some View {
NavigationLink(destination: HomeDetailsView()) {
Text("Go")
}
}
}
这是 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")
})
}
}
这样您就可以关闭选项卡视图并从项目的任何视图转到根视图。 :)
我的导航流程:
在这里,我的 View A
到 View G
在一个导航视图下。
NavigationView {
ViewA()
}
然后从 View D
和 View G
我正在通过模态移动到我的 TabView H
,像这样:
Button(action: {
isPresented.toggle()
}, label: {
Text("GO!")
})
.fullScreenCover(isPresented: $isPresented) {
TabbarView()
}
在我的选项卡视图中,所有视图都有自己的导航视图,如下所示:
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"))
现在我想返回 viewA
,比如从 Home View
按 Sign Out
按钮。我试过这个,只是想看看它是否让我回到之前的视图,但它不起作用。
struct HomeView: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
Button(action: {
self.presentationMode.wrappedValue.dismiss()
}, label: {
Text("Dismiss")
})
}
}
那么我怎样才能关闭 tabview 并返回到我的 Root view A
?
我终于成功了。要回滚到 Root 视图,我使用了这个:
NavigationLink(destination: <#T##_#>, tag: <#T##Hashable#>, selection: <#T##Binding<Hashable?>#>, label: <#T##() -> _#>)
为了关闭呈现的视图,我使用了这个:
UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true, completion: nil)
老实说,这很简单。 我们需要做的就是 NavigationLink selection
,在这种情况下是selectedItem nil
& dismiss
整个项目的模式。 所有这些都是在 @EnvironmentObject
首先创建 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
}
}
}
现在,让我们创建 ViewA
,即 AuthListView
:
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)
}
}
这是 auth 视图的每个单元格:
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)
}
}
在导航视图下的 ContentView
中加载此视图:
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
var body: some View {
NavigationView {
AuthListView()
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
到目前为止,我们可以从 ViewA
推送到 ViewB
。在这里,我只展示了 ViewA
push> ViewB
push> ViewC
present> TabView
> 的导航流程,然后 Dismiss TabView
from HomeView
并返回根 ViewA
,因为其他视图的其余部分将遵循相同的。它也适用于任何选项卡栏视图的子导航。所以让我们创建 ViewB
(PhoneView
) 并推送到 ViewC
(PINView
):
视图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")
}
}
}
视图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()
}
}
}
到目前为止,我们已经展示了之前 ViewC
中的选项卡视图。这是我们的选项卡视图:
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"))
}
}
现在,如果我想关闭显示的选项卡视图并通过按 HomeView
中的 sign out
按钮返回根视图,我所要做的就是调用 tabbarViewModel.gotoRootView()
像这样:
struct HomeView: View {
@EnvironmentObject var tabbarViewModel: TabbarViewModel
var body: some View {
Button(action: {
tabbarViewModel.gotoRootView()
}, label: {
Text("Sign Out")
})
}
}
我也可以关闭选项卡视图并从我的 HomeView 的子视图转到根视图。让我们从 HomeView 转到子视图:
struct HomeView: View {
var body: some View {
NavigationLink(destination: HomeDetailsView()) {
Text("Go")
}
}
}
这是 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")
})
}
}
这样您就可以关闭选项卡视图并从项目的任何视图转到根视图。 :)