
為什么要用到Router去做跳轉(zhuǎn):解耦、方便
像底下這種代碼可能寫(xiě)了很多很多遍了,尤其是在項(xiàng)目中某個(gè)頁(yè)面入口很多的情況下。
let xx = XX()
...
let vc = XXXViewController(xx:xxx)
self.navigationController?.pushViewController(vc, animated: true)
尤其是公司要開(kāi)發(fā)多個(gè)項(xiàng)目,對(duì)模塊進(jìn)行了拆分,組件化的模式需要中間一個(gè)Router去決定跳轉(zhuǎn)到那個(gè)模塊的頁(yè)面,而不是在每個(gè)頁(yè)面都import XX 耦合非常嚴(yán)重。
瀏覽了下GitHub上的兩個(gè)庫(kù),都不是很滿意
-
Router 比較Swifty,但是耦合比較嚴(yán)重,實(shí)現(xiàn)了很多暫時(shí)不需要的功能,每個(gè)VC需要將自己當(dāng)成字符串告訴Router , 還要告訴當(dāng)前的navigation , 使用了
NSClassFromString根據(jù)字符創(chuàng)建的 AnyClass 。比較繁瑣-- 但是還有很好的思想可以借鑒的 -
FNUrlRoute 通過(guò)url形式跳轉(zhuǎn),看起來(lái)挺不錯(cuò),仔細(xì)看下代碼,首先
AppDelegate要先用字符串和VC進(jìn)行映射,每個(gè)VC要傳入[String:AnyObject]?進(jìn)行初始化,這個(gè)把VC的創(chuàng)建都限制死了。絕對(duì)用不了呀,還有那么多人star ...
說(shuō)完他們的不足,首先來(lái)看下我們這個(gè)Router設(shè)計(jì)要求
- 解耦:調(diào)用者不知道VC的名字
- 不要用字符串,字符串容易出錯(cuò)
- 不能限制初始化方法
- 調(diào)用者應(yīng)該非常簡(jiǎn)潔
那么不使用字符串很容易想到就是用枚舉來(lái)替代,枚舉中也可能映射VC呀,而且不用在AppDelegate中注冊(cè),不能限制初始化方法我們就用params字典去映射 Router 里面的方式 感覺(jué)比較好 , 自己肯定是知道自己怎么初始化的, 用協(xié)議的方式比用強(qiáng)迫字典初始化好很多
public typealias RouterParameter = [String: Any]
public protocol Routable {
/**
類(lèi)的初始化方法
- params 傳參字典
*/
static func initWithParams(params: RouterParameter?) -> UIViewController
}
我們跳轉(zhuǎn)只需要知道哪個(gè)VC要傳的參數(shù),這個(gè)都交給枚舉就可以了,為了項(xiàng)目路徑映射和跳轉(zhuǎn)解耦,用一個(gè)協(xié)議
public protocol RouterPathable {
var any: AnyClass { get }
var params: RouterParameter? { get }
}
其他就交給跳轉(zhuǎn)了
open class func open(_ path:RouterPathable , present: Bool = false , animated: Bool = true , presentComplete: (()->Void)? = nil){
if let cls = path.any as? Routable.Type {
let vc = cls.initWithParams(params: path.params)
vc.hidesBottomBarWhenPushed = true
let topViewController = RouterUtils.currentTopViewController()
if topViewController?.navigationController != nil && !present {
topViewController?.navigationController?.pushViewController(vc, animated: animated)
}else{
topViewController?.present(vc, animated: animated , completion: presentComplete)
}
}
}
其中有用到一個(gè)工具方法是獲取最上層的vc好進(jìn)行跳轉(zhuǎn),具體代碼可以去GitHub下載
使用: 需要使用router進(jìn)行跳轉(zhuǎn)的都要事先Routable 接口,調(diào)用者不需要
無(wú)參數(shù)
class AVC: UIViewController, Routable{
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.red
}
static func initWithParams(params: RouterParameter?) -> UIViewController {
return AVC()
}
}
有參數(shù)
struct Demo {
var name: String
var id: Int
}
class RVC: UIViewController {
let demo:Demo
init(demo:Demo) {
self.demo = demo
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
navigationItem.title = demo.name
}
}
extension RVC: Routable {
static func initWithParams(params: RouterParameter?) -> UIViewController {
guard let demo = params?["demo"] as? Demo else {
fatalError("params is wrong")
}
let rvc = RVC(demo: demo)
return rvc
}
}
路徑映射:
比如上面的vc都是其他模塊的,那么只有這個(gè)映射的枚舉需要引入其他模塊,調(diào)用者不需要 , 下面展示三個(gè)vc的路徑映射
enum RouterPath: RouterPathable {
case avc
case bvc(String)
case rvc(Demo)
var any: AnyClass {
switch self {
case .avc:
return AVC.self
case .bvc:
return BVC.self
case .rvc:
return RVC.self
}
}
var params: RouterParameter? {
switch self {
case .bvc(let name):
return ["name":name]
case .rvc(let demo):
return ["demo":demo]
default:
return nil
}
}
}
只要實(shí)現(xiàn) RouterPathable 都可以 ,如果需要映射的vc特別多 , 也可以分組管理。
調(diào)用者就很方便了
Router.open(RouterPath.avc)
let demo = Demo(name: "RVC title", id: 1)
Router.open(RouterPath.rvc(demo))
或者present
Router.open(RouterPath.bvc("BVC title"), present: true)
項(xiàng)目地址: SwiftyRouter