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/") { 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的切换。

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

你可能感兴趣的:(URLNavigator原理和应用)