Router
為什么要用到Router去做跳轉:解耦、方便
像底下這種代碼可能寫了很多很多遍了,尤其是在項目中某個頁面入口很多的情況下。
let xx = XX()
...
let vc = XXXViewController(xx:xxx)
self.navigationController?.pushViewController(vc, animated: true)
尤其是公司要開發多個項目,對模塊進行了拆分,組件化的模式需要中間一個Router去決定跳轉到那個模塊的頁面,而不是在每個頁面都import XX 耦合非常嚴重。
瀏覽了下GitHub上的兩個庫,都不是很滿意
-
Router 比較Swifty,但是耦合比較嚴重,實現了很多暫時不需要的功能,每個VC需要將自己當成字符串告訴Router , 還要告訴當前的navigation , 使用了
NSClassFromString
根據字符創建的 AnyClass 。比較繁瑣-- 但是還有很好的思想可以借鑒的 -
FNUrlRoute 通過url形式跳轉,看起來挺不錯,仔細看下代碼,首先
AppDelegate
要先用字符串和VC進行映射,每個VC要傳入[String:AnyObject]?
進行初始化,這個把VC的創建都限制死了。絕對用不了呀,還有那么多人star ...
說完他們的不足,首先來看下我們這個Router設計要求
- 解耦:調用者不知道VC的名字
- 不要用字符串,字符串容易出錯
- 不能限制初始化方法
- 調用者應該非常簡潔
那么不使用字符串很容易想到就是用枚舉來替代,枚舉中也可能映射VC呀,而且不用在AppDelegate
中注冊,不能限制初始化方法我們就用params字典去映射 Router 里面的方式 感覺比較好 , 自己肯定是知道自己怎么初始化的, 用協議的方式比用強迫字典初始化好很多
public typealias RouterParameter = [String: Any]
public protocol Routable {
/**
類的初始化方法
- params 傳參字典
*/
static func initWithParams(params: RouterParameter?) -> UIViewController
}
我們跳轉只需要知道哪個VC要傳的參數,這個都交給枚舉就可以了,為了項目路徑映射和跳轉解耦,用一個協議
public protocol RouterPathable {
var any: AnyClass { get }
var params: RouterParameter? { get }
}
其他就交給跳轉了
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)
}
}
}
其中有用到一個工具方法是獲取最上層的vc好進行跳轉,具體代碼可以去GitHub下載
使用: 需要使用router進行跳轉的都要事先Routable
接口,調用者不需要
無參數
class AVC: UIViewController, Routable{
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.red
}
static func initWithParams(params: RouterParameter?) -> UIViewController {
return AVC()
}
}
有參數
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都是其他模塊的,那么只有這個映射的枚舉需要引入其他模塊,調用者不需要 , 下面展示三個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
}
}
}
只要實現 RouterPathable
都可以 ,如果需要映射的vc特別多 , 也可以分組管理。
調用者就很方便了
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)
項目地址: SwiftyRouter