SwiftUI: Pop to root view when selected tab is tapped again

后端 未结 3 940
轮回少年
轮回少年 2020-12-03 16:08

Starting point is a NavigationView within a TabView. I\'m struggling with finding a SwiftUI solution to pop to the root view within the navigation stack when the selected ta

3条回答
  •  温柔的废话
    2020-12-03 16:56

    Here's how I did it:

    struct UIKitTabView: View {
        var viewControllers: [UIHostingController]
    
        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(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
            tabBarController.delegate = context.coordinator
            return tabBarController
        }
    
        func updateUIViewController(_ uiViewController: UITabBarController, context: Context) { }
    }
    
    extension TabBarController {
        func makeCoordinator() -> TabBarController.Coordinator {
            Coordinator(self)
        }
        class Coordinator: NSObject, UITabBarControllerDelegate {
            var parent: TabBarController
            init(_ parent: TabBarController){self.parent = parent}
            var previousController: UIViewController?
            private var shouldSelectIndex = -1
    
            func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
                shouldSelectIndex = tabBarController.selectedIndex
                return true
            }
    
            func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
                if shouldSelectIndex == tabBarController.selectedIndex {
                    if let navVC = tabBarController.viewControllers![shouldSelectIndex].nearestNavigationController {
                        if (!(navVC.popViewController(animated: true) != nil)) {
                            navVC.viewControllers.first!.scrollToTop()
                        }
                    }
                }
            }
        }
    }
    
    extension UIViewController {
        func scrollToTop() {
            func scrollToTop(view: UIView?) {
                guard let view = view else { return }
                switch view {
                case let scrollView as UIScrollView:
                    if scrollView.scrollsToTop == true {
                        scrollView.setContentOffset(CGPoint(x: 0.0, y: -scrollView.safeAreaInsets.top), animated: true)
                        return
                    }
                default:
                    break
                }
    
                for subView in view.subviews {
                    scrollToTop(view: subView)
                }
            }
            scrollToTop(view: view)
        }
    }
    

    Then in ContentView.swift I use it like this:

    struct ContentView: View {
        var body: some View {
            ZStack{
                UIKitTabView([
                    UIKitTabView.Tab(
                        view: FirstView().edgesIgnoringSafeArea(.top),
                        barItem: UITabBarItem(title: "Tab1", image: UIImage(systemName: "star"), selectedImage: UIImage(systemName: "star.fill"))
                    ),
                    UIKitTabView.Tab(
                        view: SecondView().edgesIgnoringSafeArea(.top),
                        barItem: UITabBarItem(title: "Tab2", image: UIImage(systemName: "star"), selectedImage: UIImage(systemName: "star.fill"))
                    ),
                ])
    
            }
        }
    }
    

    Note that when the user is already on the root view, it scrolls to top automatically

提交回复
热议问题