切换标签时,TabView重置导航堆栈

时间:2019-09-03 12:50:26

标签: swiftui

我有一个简单的TabView:

TabView {
    NavigationView {
        VStack {
            NavigationLink(destination: Text("Detail")) {
                Text("Go to detail")
            }
        }
    }
        .tabItem { Text("First") }
        .tag(0)
    Text("Second View")
        .tabItem { Text("Second") }
        .tag(1)
}

当我转到选项卡1上的详细信息视图时,切换到选项卡2,然后再切换回选项卡1,我假设将返回到详细信息视图(iOS中随处可见的基本UX)。而是重置为选项卡1的根视图。

由于SwiftUI似乎不支持此功能,我该如何解决?

3 个答案:

答案 0 :(得分:10)

这里不太明显的解决方案是实际上不使用SwiftUI。为了获得UIKit行为,我将UIKit UITabBarController包裹在SwiftUI UIViewControllerRepresentable中,例如以下示例:https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit

我在这里展示一个基本的实现。完整的最新实现在github上:https://gist.github.com/Amzd/2eb5b941865e8c5cccf149e6e07c8810

在SwiftUI视图中包装UIKit UITabBarController:

struct UIKitTabView: View {
    var viewControllers: [UIHostingController<AnyView>]

    init(_ tabs: [Tab]) {
        self.viewControllers = tabs.map {
            let host = UIHostingController(rootView: $0.view)
            host.tabBarItem = $0.barItem
            return host
        }
    }

    var body: some View {
        TabBarController(controllers: viewControllers)
            .edgesIgnoringSafeArea(.all)
    }

    struct Tab {
        var view: AnyView
        var barItem: UITabBarItem

        init<V: View>(view: V, barItem: UITabBarItem) {
            self.view = AnyView(view)
            self.barItem = barItem
        }
    }
}
struct TabBarController: UIViewControllerRepresentable {
    var controllers: [UIViewController]

    func makeUIViewController(context: Context) -> UITabBarController {
        let tabBarController = UITabBarController()
        tabBarController.viewControllers = controllers
        return tabBarController
    }
}

用法示例:

struct ExampleView: View {
    @State var text: String = ""

    var body: some View {
        UIKitTabView([
            UIKitTabView.Tab(
                view: NavView(), 
                barItem: UITabBarItem(title: "First", image: nil, selectedImage: nil)
            ),
            UIKitTabView.Tab(
                view: Text("Second View"), 
                barItem: UITabBarItem(title: "Second", image: nil, selectedImage: nil)
            )
        ])
    }
}

struct NavView: View {
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: Text("This page stays when you switch back and forth between tabs (as expected on iOS)")) {
                    Text("Go to detail")
                }
            }
        }
    }
}

答案 1 :(得分:1)

因此,这会在切换选项卡时“保留”详细信息视图,但只能通过在切换回选项卡1时明显地推动详细信息视图来实现。我无法通过.animation()来禁用它。

此外,您几乎必须覆盖DetailView中的导航栏项,因为默认的后退按钮的行为很奇怪(注释.navigationBarItems()行以了解我的意思)。

有了这些警告,这确实可以解决。

struct ContentView: View {
    @State var showingDetail = false

    var body: some View {
        TabView {
            NavView(showingDetail: $showingDetail)
                .tabItem { Text("First") }
                .tag(0)
            Text("Second View")
                .tabItem { Text("Second") }
                .tag(1)
        }
    }
}

struct NavView: View {
    @Binding var showingDetail: Bool

    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination: DetailView(showing: $showingDetail), isActive: $showingDetail) {
                    Text("Go to detail")
                }
            }
        }
    }
}

struct DetailView: View {
    @Binding var showing: Bool

    var body: some View {
            Text("Detail")
                .navigationBarItems(leading: Button("Back", action: { self.showing = false }))
    }
}

答案 2 :(得分:0)

这是一个简单的示例,说明如何保留导航堆栈的状态,其根目录为项目列表:

struct ContentView: View {

    var body: some View {

        TabView {

            Text("First tab")
                .tabItem { Image(systemName: "1.square.fill"); Text("First") }
                .tag(0)

            SecondTabView()
                .tabItem { Image(systemName: "2.square.fill"); Text("Second") }
                .tag(1)
        }
    }
}

struct SecondTabView: View {

    private struct ListItem: Identifiable {
        var id = UUID()
        let title: String
    }

    private let items = (1...10).map { ListItem(title: "Item #\($0)") }

    @State var selectedItemIndex: Int? = nil

    var body: some View {

        NavigationView {
            List(self.items.indices) { index in
                NavigationLink(destination:  Text(self.items[index].title),
                               tag: index,
                               selection: self.$selectedItemIndex) {
                    Text(self.items[index].title)
                }
            }
            .navigationBarTitle("Second tab", displayMode: .inline)
        }
    }
}