繁体   English   中英

如何从 TabView 的子视图导航回 SwiftUI 中的 TabView?

[英]How to navigate from a subview of a TabView back to the TabView in SwiftUI?

在 SwiftUI 中,TabView 必须是根视图。 因此,您不能使用 NavigationLink 导航到 TabView。 例如,假设我的应用程序中有四个屏幕。

屏幕 A 是一个 TabView,包含屏幕 B 和屏幕 C。 屏幕 B 是具有 NavigationLink 的列表,可将您带到列表项详细信息(屏幕 D)屏幕 C 是信息视图(在问题中并不重要)屏幕 D 是列表项详细信息屏幕,您必须先导航到屏幕b 到这里。 然而,屏幕 D 有一个按钮,应该在 ViewModel 中执行网络操作,然后在完成后将您带到屏幕 A。

屏幕 D 如何导航回两个级别到根屏幕(屏幕 A)?

我做了这样的把戏,为我工作。

在 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()
}

注意:我希望在NavigationView中嵌入TabView会做同样的工作,但没有奏效,这是做这个技巧的原因。

我假设, 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()
            })
        }
    }
}

“弹出”到根视图的一种有效方法是在用于导航的NavigationLink上使用isDetailLink修饰符。

默认情况下, isDetailLinktrue 此修改器用于各种视图容器,例如 iPad,其中详细视图将显示在右侧。

isDetailLink设置为false意味着视图将被推送到NavigationView堆栈的顶部,也可以被推送出去。

除了在NavigationLink isDetailLink设置为 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")
    }
}

我已经用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)
        )

    }
}

暂无
暂无

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

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