路由
用法
假设您有一个显示用户选择单元格时显示权限信息的表视图控制器。TableView:didSelectRowAtIndexPath: 的实现可能如下所示。
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
switch Row(rawValue: indexPath.row)! {
// ...
case .PrivilegedInfo:
router["Views"].open("routingexample://push/privilegedinfo")
}
// ...
}
或许权限信息仅在用户使用该服务进行认证后才开始提供。登录后,我们希望立即向用户展示权限信息。在不更改上述实现的情况下,我们可以代理意图并显示登录视图,然后回调可以显示权限信息屏幕。
router.proxy("/*/privilegedinfo", tags: ["Views"]) { route, parameters, any, next in
if authenticated {
next(nil)
} else {
next(("routingexample://present/login?callback=\(route)", parameters, any))
}
}
最终,我们可能需要支持从应用外部路由到权限信息的深度链接。这可以在 AppDelegate 中简单地处理如下。
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
return router["Views"].open(url)
}
一个应用程序中其他路由的示例可能如下所示。
let presentationSetup: PresentationSetup = { vc, _, _ in
vc.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel,
target: vc,
action: #selector(vc.cancel))
}
router.map("routingexample://present/login",
source: .storyboard(storyboard: "Main", identifier: "LoginViewController", bundle: nil),
style: .inNavigationController(.present(animated: true)),
setup: presentationSetup)
router.map("routingexample://push/privilegedinfo",
source: .storyboard(storyboard: "Main", identifier: "PrivilegedInfoViewController", bundle: nil),
style: .push(animated: true))
router.map("routingexample://present/settings",
source: .storyboard(storyboard: "Main", identifier: "SettingsViewController", bundle: nil),
style: .inNavigationController(.present(animated: true)),
setup: presentationSetup)
router.proxy("/*", tags: ["Views"]) { route, parameters, any, next in
print("opened: route (\(route)) with parameters (\(parameters)) & passing (\(any))")
next(nil)
}
在最简单的形式中,路由允许将字符串模式关联到闭包。这允许在代码的某些区域表达意图并在另一处实现它。UI 可能只关心表达转换为另一个视图的意图,而业务逻辑可能由其他地方处理。通过映射和代理,路由允许通过映射显式记录应用程序的行为和视图。
安装
CocoaPods
通过 CocoaPods
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '10.0'
use_frameworks!
pod 'Routing', '~> 1.4.0'
Carthage
通过 Carthage
github "jjgp/Routing"
详细信息
地图
路由实例可以将字符串模式映射到视图控制器导航,如上文的《用法》部分所述,或仅映射为一个闭包,如下所示。闭包将拥有四个参数。匹配的路由、参数(URL中的查询和片段)、通过打开传递的任何数据,和必须被调用的完成闭包,或者路由器将停止所有后续的 #open 调用。
router.map("routingexample://route/:argument") { route, parameters, any, completed in
argument = parameters["argument"]
completed()
}
代理
路由实例可以代理任何字符串模式。闭包也将拥有四个参数。匹配的路由、参数、任何通过代理传递的数据,以及一个下一层闭包。下一层闭包接受一个包含 String、Parameters 和 Any? 的 ProxyCommit? 元组。如果向 Next 传递了 nil,则路由器将继续到另一个代理(如果存在)或随后映射到路由。如果代理向下一层闭包传递了 ProxyCommit 元组,则路由器将跳过任何后续代理并尝试匹配到映射的路由。未能调用下一层将停止路由器以及所有后续的 #open 调用。
router.proxy("routingexample://route/one") { route, parameters, any, next -> Void in
next(("routingexample://route/two", parameters, any))
}
映射或代理的顺序
在一般情况下,最后调用注册映射或代理到路由器的接口将首先在相应的匹配URL事件中调用。代理将首先被处理,然后是映射。
标签
可以将标签传递给映射或代理。用于视图控制器导航的映射的默认标签是 "视图"。标签允许路由器订阅到特定上下文。如果路由器订阅了 "视图",则它将只尝试查找标记为这样的路由。
router.proxy("/*", tags: ["Views, Logs"]) { route, parameters, any, next in
print("opened: route (\(route)) with parameters (\(parameters)) & passing (\(any))")
next(nil)
}
router["Views", "Logs", "Actions"].open(url)
router["Views"].open(url, passing: NSDate()) // pass any data if needed
router.open(url) // - or - to search all routes...
路由拥有者
在使用 #map 或 #proxy 时可以为路由指定 RouteOwner。当 RouteOwner 被释放时,路由将从 Routing 实例中删除。
public protocol RouteOwner: class {}
class PrivilegedInfoViewController: UIViewController, RouteOwner {
override func viewDidLoad() {
router.map("routingexample://secret",
owner: self,
source: .storyboard(storyboard: "Main", identifier: "SecretViewController", bundle: nil),
style: .push(animated: true))
}
}
RouteUUID 和路由释放
通过 #map 或 #proxy 添加路由时,将返回一个 RouteUUID。此 RouteUUID 可用于释放路由。
routeUUID = router.map("routingexample://present/secret",
source: .storyboard(storyboard: "Main", identifier: "SecretViewController", bundle: nil),
style: .inNavigationController(.present(animated: true)))
router.dispose(of: routeUUID)
回调队列
可以向映射或代理传递一个队列。此队列将是调用 RouteHandler 或 ProxyHandler 闭包的队列。默认情况下,用于视图控制器导航的映射将在主队列上调回。
let callbackQueue = DispatchQueue(label: "Call Back Queue", attributes: [])
router.map("routingexample://route", queue: callbackQueue) { _, _, _, completed in
completed()
}
演示设置
路由器映射的视图控制器将有机会通过闭包或RoutingPresentationSetup协议了解已打开的路由。在任一实现中,视图控制器都将能够访问通过URL传递的参数。上述示例中关于使用方法的闭包方法即为一例。该协议如下所示。
class LoginViewController: UIViewController, RoutingPresentationSetup {
var callback: String?
func setup(_ route: String, with parameters: Parameters, passing any: Any?) {
if let callbackURL = parameters["callback"] {
self.callback = callbackURL
}
if let date = any as? NSDate {
self.passedDate = date
}
}
}
演示样式
indirect public enum PresentationStyle {
case show
case showDetail
case present(animated: Bool)
case push(animated: Bool)
case custom(custom: (presenting: UIViewController, presented: UIViewController, completed: Routing.Completed) -> Void)
case inNavigationController(Routing.PresentationStyle)
}
上述演示样式已提供。递归的.InNavigationController(PresentationStyle)枚举将导致视图控制器在以任意方式显示之前被包裹在导航控制器中。同时,也有提供自定义演示样式的功能。
视图控制器来源
以下视图控制器来源被使用。
public enum ControllerSource {
case storyboard(storyboard: String, identifier: String, bundle: NSBundle?)
case nib(controller: UIViewController.Type, name: String?, bundle: NSBundle?)
case provided(() -> UIViewController)
}
演示扩展
以下已扩展,以允许传递完成闭包。
extension UIViewController {
public func showViewController(vc: UIViewController, sender: AnyObject?, completion: Routing.Completed)
public func showDetailViewController(vc: UIViewController, sender: AnyObject?, completion: Routing.Completed)
}
extension UINavigationController {
public func pushViewController(vc: UIViewController, animated: Bool, completion: Routing.Completed)
public func popViewControllerAnimated(animated: Bool, completion: Routing.Completed) -> UIViewController?
public func popToViewControllerAnimated(viewController: UIViewController, animated: Bool, completion: Routing.Completed) -> [UIViewController]?
public func popToRootViewControllerAnimated(animated: Bool, completion: Routing.Completed) -> [UIViewController]?
}