在SwiftUI中,TabView必须是根视图。因此,您不能使用NavigationLink导航到TabView。假设我的应用程序中有四个屏幕。
屏幕A是一个TabView,其中包含屏幕B和屏幕C。 屏幕B是具有NavigationLink的列表,可带您进入列表项的详细信息(屏幕D) 屏幕C是一个信息视图(在问题中并不重要) 屏幕D是列表项详细信息屏幕,您必须首先导航到屏幕b才能到达此处。但是,屏幕D具有一个按钮,该按钮应在ViewModel中执行网络操作,然后在完成时将您带到ScreenA。
屏幕D如何导航到根屏幕(屏幕A)的两个级别?
答案 0 :(得分:1)
我做了这样的把戏,为我工作。
在SceneDelegate.swift中,我修改了自动生成的代码。
let contentView = ContentView()
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
// Trick here.
let nav = UINavigationController(
rootViewController: UIHostingController(rootView: contentView)
)
// I embedded this host controller inside UINavigationController
// window.rootViewController = UIHostingController(rootView: contentView)
window.rootViewController = nav
self.window = window
window.makeKeyAndVisible()
}
注意:我希望在TabView
内嵌入NavigationView
可以完成相同的工作,但是没有用,这就是这样做的原因。
我假设contentView
是要弹出的视图(其中一个包含TabView)
然后在从该视图导航的任何视图中,您可以调用
extension View {
func popToRoot() {
guard let rootNav = UIApplication.shared.windows.first?.rootViewController as? UINavigationController else { return }
rootNav.popToRootViewController(animated: true)
}
}
// Assume you eventually came to this view.
struct DummyDetailView: View {
var body: some View {
Text("DetailView")
.navigationBarItems(trailing:
Button("Pop to root view") {
self.popToRoot()
}
)
}
}
// EDIT: Requested sample with a viewModel
struct DummyDetailViewWithViewModel: View {
var viewModel: SomeViewModel = SomeViewModel()
var body: some View {
Button("Complete Order!!") {
viewModel.completeOrder(success: { _ in
print("Order Completed")
self.popToRoot()
})
}
}
}
答案 1 :(得分:1)
“弹出”根视图的一种有效方法是利用用于导航的isDetailLink
上的NavigationLink
修饰符。
默认情况下,isDetailLink
是true
。此修饰符用于各种视图容器,例如在iPad上,详细视图将显示在右侧。
将isDetailLink
设置为false
意味着该视图将被推到NavigationView
堆栈的顶部,也可以被推下。
在isDetailLink
上将NavigationLink
设置为false,然后将isActive
绑定传递到每个子目标视图。要弹出到根视图时,请将值设置为false
,它将弹出所有内容:
import SwiftUI
struct ScreenA: View {
@State var isActive : Bool = false
var body: some View {
NavigationView {
NavigationLink(
destination: ScreenB(rootIsActive: self.$isActive),
isActive: self.$isActive
) {
Text("ScreenA")
}
.isDetailLink(false)
.navigationBarTitle("Screen A")
}
}
}
struct ScreenB: View {
@Binding var rootIsActive : Bool
var body: some View {
NavigationLink(destination: ScreenD(shouldPopToRootView: self.$rootIsActive)) {
Text("Next screen")
}
.isDetailLink(false)
.navigationBarTitle("Screen B")
}
}
struct ScreenD: View {
@Binding var shouldPopToRootView : Bool
var body: some View {
VStack {
Text("Last Screen")
Button (action: { self.shouldPopToRootView = false } ){
Text("Pop to root")
}
}.navigationBarTitle("Screen D")
}
}
答案 2 :(得分:0)
我已经用self.presentationMode.wrappedValue.dismiss()
解决了这个问题。这是调用此方法以返回到导航根视图的方法。在这里,TestView是ScreenA,ItemList是ScreenB,InfoView是ScreenC,ItemDetails是ScreenD。
import SwiftUI
struct TestView: View {
@State private var currentTab: Tab = .list
var body: some View {
TabView(selection: $currentTab){
ItemList()
.tabItem{
Text("List")
}
.tag(Tab.list)
.navigationBarHidden(false)
InfoView()
.tabItem{
Text("Info")
}
.tag(Tab.info)
.navigationBarHidden(true)
}
}
}
struct ItemList: View {
var body: some View {
VStack{
NavigationView{
List {
NavigationLink(destination: ItemDetails()){
Text("Item")
}
NavigationLink(destination: ItemDetails()){
Text("Item")
}
NavigationLink(destination: ItemDetails()){
Text("Item")
}
NavigationLink(destination: ItemDetails()){
Text("Item")
}
}.navigationBarTitle("Item List")
}
}
}
}
struct InfoView: View {
var body: some View {
Text("This is information view")
}
}
struct ItemDetails: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@State private var loading = false
var body: some View {
ZStack {
Text("Connecting...")
.font(.title)
.offset(y: -150)
.pulse(loading: self.loading)
VStack {
Text("This is Item Details")
Button("Connect"){
self.loading = true
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.loading = false
self.presentationMode.wrappedValue.dismiss()
}
}.padding()
}
}
}
}
enum Tab {
case list, info
}
extension View {
func pulse(loading: Bool) -> some View {
self
.opacity(loading ? 1 : 0)
.animation(
Animation.easeInOut(duration: 0.5)
.repeatForever(autoreverses: true)
)
}
}