路由
路由
和路由处理器
路由器基于开放导航流程的概念,其中应用内部任何位置都可以通过传递对应预先注册的路由处理器的路由
类型实例在任何地方到达。虽然路由
提供有关目标所需的信息,路由处理器将提供可以指定为AnyView
、UIViewController
、NSViewController
或其他任何类型的通用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
-> 视图应以模态方式呈现。
AppRouter
和 RouteHandler
的注册
框架提供了一个 Router
抽象类的实现,即 AppRouter
。虽然可以通过 Router
的 register
方法注册处理程序,但最容易的方法是将 AppRouter
使其符合 RouteHandlerRegistering
。此时,AppRouter
将在首次访问其 root
属性时调用 registerRoutes
。
extension AppRouter: RouteHandlerRegistering where View == AnyView {
public func registerRoutes() {
register(HomeRouteHandler())
}
}
AppRouter
的 Navigator
类型
导航和 注册 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
}
}
}
}