路由-SebastianPickl 0.2.1

路由-SebastianPickl 0.2.1

Sebastian Pickl 维护。



路由

路由路由处理器

路由器基于开放导航流程的概念,其中应用内部任何位置都可以通过传递对应预先注册的路由处理器的路由类型实例在任何地方到达。虽然路由提供有关目标所需的信息,路由处理器将提供可以指定为AnyViewUIViewControllerNSViewController或其他任何类型的通用View类型的实例。

public struct HomeRoute: Route {
    public static var transition: Transition = .root(
        route: HomeRoute(),
        rootItem: RootItem(
            title: "Home",
            image: UIImage(systemName: "house"),
            selectedImage: UIImage(systemName: "house.fill")
        )
    )
}

public struct HomeRouteHandler: RouteHandler {
    public func view(for route: HomeRoute) -> AnyView {
        AnyView(Home())
    }
}

路由类型视为接口的一部分,例如主屏幕等特征模块的接口,而它们的相应处理器是只对从单独的应用程序模块进行注册(如路由器)的实现细节。

转换

路由提供特定的转换,该转换确定了视图(由路由处理器返回)的展示方式。当前可能的转换有:

  • .root -> 视图是初始展示的视图集合的一部分,例如标签栏中的标签。
  • .stack -> 视图应推送到当前导航堆栈。
  • .modal -> 视图应以模态方式呈现。

AppRouterRouteHandler 的注册

框架提供了一个 Router 抽象类的实现,即 AppRouter。虽然可以通过 Routerregister 方法注册处理程序,但最容易的方法是将 AppRouter 使其符合 RouteHandlerRegistering。此时,AppRouter 将在首次访问其 root 属性时调用 registerRoutes

extension AppRouter: RouteHandlerRegistering where View == AnyView {
    public func registerRoutes() {
        register(HomeRouteHandler())
    }
}

导航和 AppRouterNavigator 类型

注册 RouteHandler 后,通过将 Route 实例传递给 Router 来启动导航操作。Router 本身只负责将 Route 与其处理程序相匹配。AppRouter 将继续调用处理程序的 view 方法,并将返回的值传递给一个 Navigator。这可以被看作是最终执行导航行动的 UI 组件。

一个例子 Navigator 可能是一个 UITabBarController。特定的 Navigator 协议实现是提供导航逻辑的地方。

import UIKit

extension UITabBarController: Navigator {
    public var root: UIViewController {
        self
    }

    public func setUp(rootElements: [(UIViewController, RootItem)]) {
        let navigationControllers = rootElements
            .map { viewController, rootItem -> UINavigationController in
                let navigationController = UINavigationController(rootViewController: viewController)
                navigationController.tabBarItem = .init(
                    title: rootItem.title,
                    image: rootItem.image,
                    selectedImage: rootItem.selectedImage
                )
                return navigationController
            }
        setViewControllers(navigationControllers, animated: false)
    }

    public func callAsFunction(view viewController: @autoclosure () -> UIViewController, transition: Transition) {
        switch transition {
        case .stack:
            let viewController = viewController()
            let push: () -> Void = {
                (self.selectedViewController as? UINavigationController)?.pushViewController(viewController, animated: true)
            }
            if presentedViewController != nil {
                dismiss(animated: true, completion: push)
            } else {
                push()
            }
        case .modal:
            present(viewController(), animated: true, completion: nil)
        case .root(route: _, rootItem: let rootItem):
            if let index = viewControllers?.firstIndex(where: { viewController in
                viewController.tabBarItem.title == rootItem.title
            }) {
                selectedIndex = index
            }
        }
    }
}