URLNavigator原理和应用

URLNavigator是Swift的一个三方库,用来实现页面的导航。可以通过字符串的形式路由到一个新的页面(UIViewController),不需要引入这个新页面,从而实现解耦,以及动态路由。
试用URLNavigator,需要以下几个步骤:

1.注册路由

navigator.register("YXRouter://VC/TravelPlan") { url, values, context in
  return TravelPlanViewController()
}

其中navigator是Navigator类型的对象。Navigator遵循NavigatorType协议,其声明了2个属性matcher,delegate,和register等若干方法(后面需要的时候再详细介绍),在类Navigator中register方法定义如下:

  open func register(_ pattern: URLPattern, _ factory: @escaping ViewControllerFactory) {
    self.viewControllerFactories[pattern] = factory
  }

两个参数的类型定义如下:

public typealias URLPattern = String
public typealias ViewControllerFactory = (_ url: URLConvertible, _ values: [String: Any], _ context: Any?) -> UIViewController?

函数只有1行代码,

self.viewControllerFactories[pattern] = factory。

其中viewControllerFactories的定义是一个字典类型,key是URLPattern类型(也就是String),ViewControllerFactory就是上面的闭包类型。

 private var viewControllerFactories = [URLPattern: ViewControllerFactory]()

整个reigster的含义就是,在Navigator对象的字典成员变量里,添加了一个键值对,键就是URL,值是一个闭包。当URL对应的窗口被push时,对应的闭包就会得到执行。

2.打开指定页面

当要打开指定的也面时,方法调用如下:

navigator.push("YXRouter://VC/TravelPlan")

navigator还是上面定义的Navigator类型的对象。其中对push的定义,以及被其调用的方法如下(在协议NavigatorType中):

//第一个方法
//后面三个参数都有默认值
  public func push(_ url: URLConvertible, context: Any? = nil, from: UINavigationControllerType? = nil, animated: Bool = true) -> UIViewController? {
    return self.pushURL(url, context: context, from: from, animated: animated)
  }
//第二个方法
  public func pushURL(_ url: URLConvertible, context: Any? = nil, from: UINavigationControllerType? = nil, animated: Bool = true) -> UIViewController? {
    guard let viewController = self.viewController(for: url, context: context) else { return nil }
    return self.pushViewController(viewController, from: from, animated: animated)
  }
//第三个方法
  public func pushViewController(_ viewController: UIViewController, from: UINavigationControllerType?, animated: Bool) -> UIViewController? {
    guard (viewController is UINavigationController) == false else { return nil }  //判断viewController不能是UINavigationController对象,否则return nil;
    guard let navigationController = from ?? UIViewController.topMost?.navigationController else { return nil }
    guard self.delegate?.shouldPush(viewController: viewController, from: navigationController) != false else { return nil }
    navigationController.pushViewController(viewController, animated: animated)
    return viewController
  }

第一个方法不用解释,它直接调用了第二个方法。第二个方法先去获取一个viewcontroller,其定义在Navigator类中。

  open func viewController(for url: URLConvertible, context: Any? = nil) -> UIViewController? {
    let urlPatterns = Array(self.viewControllerFactories.keys)   //取viewControllerFactories字典的所有key,也就是注册的url,组成的数组。
    guard let match = self.matcher.match(url, from: urlPatterns) else { return nil } //查看是否和之前注册的url匹配的上,匹配不上直接return nil;
    guard let factory = self.viewControllerFactories[match.pattern] else { return nil }  //匹配上之后,取出配备的url对应的闭包
    return factory(url, match.values, context)    //调用这个闭包,这个闭包返回的是可选UIViewController对象
  }

还记得我们第一段代码中提供的闭包吗,返回了一个TravelPlanViewController()构造的可选对象。
获得UIViewController的对象之后,第二个方法调用了第三个方法。接下来看看第三个方法干了啥?
首先,判断viewController不能是UINavigationController对象,否则return nil;
然后,从from参数中取navigationController,如果没有则取当前最顶层的UIViewController,然后取其navigationController。
最后,用这个navigationController push viewController。

到这里,最简单的路由以及实现了。接下来我们考虑一下,切换tabbar的情况。比如从tabbar的第一个,跳到第二个item。改如何实现呢。

3.切换UITabBarController的选中项

首先,这次我们注册一个URL对应的handle,当对应的这个字符串URL模版被匹配时,对应的handle会被执行

navigator.handle("YXRouter://Table/<index>") { url, values, context in
  if let tabbarVC = currentTabBarController() as? RAMAnimatedTabBarController{
    guard let indexStr = values["index"] as? String else { return false }
    guard let index = Int(indexStr) else {return false}
    tabbarVC.setSelectIndex(from: tabbarVC.selectedIndex, to: index)
  }
  return false
}

handle方法在Navigator类中定义,跟上面介绍的register一样。

  open func handle(_ pattern: URLPattern, _ factory: @escaping URLOpenHandlerFactory) {
    self.handlerFactories[pattern] = factory
  }

第一个参数已经介绍过,第二个其实也和ViewControllerFactory一样,是个闭包类型。

public typealias URLOpenHandlerFactory = (_ url: URLConvertible, _ values: [String: Any], _ context: Any?) -> Bool

handle方法中这一句代码的含义也就是

    self.handlerFactories[pattern] = factory

向Navigator类的成员变量(一个字典类型),里面添加了一个键值对。

private var handlerFactories = [URLPattern: URLOpenHandlerFactory]()

然后,我们用下面的方法来切换tabbar

navigator.open("YXRouter://Table/3")

navigator还是上面定义的Navigator类型的对象。其中对open的定义,以及被其调用的方法如下(在协议NavigatorType中):

//第一个方法
  public func open(_ url: URLConvertible, context: Any? = nil) -> Bool {
    return self.openURL(url, context: context)
  }
//第二个方法
  public func openURL(_ url: URLConvertible, context: Any?) -> Bool {
    guard let handler = self.handler(for: url, context: context) else { return false }
    return handler()
  }
//第三个方法
  open func handler(for url: URLConvertible, context: Any?) -> URLOpenHandler? {
    let urlPatterns = Array(self.handlerFactories.keys)
    guard let match = self.matcher.match(url, from: urlPatterns) else { return nil }
    guard let handler = self.handlerFactories[match.pattern] else { return nil }
    return { handler(url, match.values, context) }
  }

这里比路由到UIViewController还要简单一些。只是执行了我们在navigator.handle()注册的闭包。直接执行这个闭包就行。这个闭包完成了对tabbar的切换。

通过这里的介绍我们可以看出,我们其实可以注册任何模版字符串,再需要的时候来调用,统一执行一系列的动作。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,941评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,397评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,345评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,851评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,868评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,688评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,414评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,319评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,775评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,945评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,096评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,789评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,437评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,993评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,107评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,308评论 3 372
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,037评论 2 355

推荐阅读更多精彩内容