SwiftUI: переход к просмотру root при повторном нажатии на выбранную вкладку - PullRequest
1 голос
/ 15 марта 2020

Начальной точкой является NavigationView в TabView. Я изо всех сил пытаюсь найти решение SwiftUI для всплывающего окна root в стеке навигации при повторном нажатии на выбранную вкладку. Во времена до SwiftUI это было так просто:

func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
    let navController = viewController as! UINavigationController
    navController.popViewController(animated: true)
}

Знаете ли вы, как можно добиться того же в SwiftUI?

В настоящее время я использую следующий обходной путь который опирается на UIKit:

if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)

            let navigationController = UINavigationController(rootViewController: UIHostingController(rootView:
                MyCustomView() // -> this is a normal SwiftUI file
                    .environment(\.managedObjectContext, context)
            ))
            navigationController.tabBarItem = UITabBarItem(title: "My View 1", image: nil, selectedImage: nil)

            // add more controllers that are part of tab bar controller

            let tabBarController = UITabBarController()
            tabBarController.viewControllers = [navigationController /*,  additional controllers */]

            window.rootViewController = tabBarController // UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }

Ответы [ 2 ]

3 голосов
/ 15 марта 2020

Здесь возможен подход. TabView

Протестировано и работает с Xcode 11.2 / iOS 13.2

* 1006 Для *1001* это дает то же поведение, что и при нажатии на другую вкладку и обратно. *

Полный код модуля:

import SwiftUI

struct TestPopToRootInTab: View {
    @State private var selection = 0
    @State private var resetNavigationID = UUID()

    var body: some View {

        let selectable = Binding(        // << proxy binding to catch tab tap
            get: { self.selection },
            set: { self.selection = $0

                // set new ID to recreate NavigationView, so put it
                // in root state, same as is on change tab and back
                self.resetNavigationID = UUID()
        })

        return TabView(selection: selectable) {
            self.tab1()
                .tabItem {
                    Image(systemName: "1.circle")
                }.tag(0)
            self.tab2()
                .tabItem {
                    Image(systemName: "2.circle")
                }.tag(1)
        }
    }

    private func tab1() -> some View {
        NavigationView {
            NavigationLink(destination: TabChildView()) {
                Text("Tab1 - Initial")
            }
        }.id(self.resetNavigationID) // << making id modifiable
    }

    private func tab2() -> some View {
        Text("Tab2")
    }
}

struct TabChildView: View {
    var number = 1
    var body: some View {
        NavigationLink("Child \(number)",
            destination: TabChildView(number: number + 1))
    }
}

struct TestPopToRootInTab_Previews: PreviewProvider {
    static var previews: some View {
        TestPopToRootInTab()
    }
}
0 голосов
/ 26 апреля 2020

Вот как я это сделал:

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

Затем в ContentView.swift я использую это так:

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"))
                ),
            ])

        }
    }
}

Обратите внимание, что когда пользователь уже на root просмотр, он автоматически прокручивается вверх

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...