思路
最基本思路: 谁显示,谁负责显示方式
即从A跳转到B,由B自己来决定是push还是present,亦或者使用自定义的转场动画来显示
所以路由界面需要遵守JJBlockRouterDestination协议并实现func showDetail(withMatchRouterResult result: JJBlockRouter.MatchResult, from sourceController: UIViewController)方法
eg:
extension SystemPushController: JJBlockRouterDestination {
func showDetail(withMatchRouterResult result: JJBlockRouter.MatchResult, from sourceController: UIViewController) {
// push
sourceController.navigationController?.pushViewController(self, animated: true)
// present
sourceController.present(self, animated: true)
// 自定义转场动画--提供使present/dismiss动画跟系统push/pop动画一致的转场动画
let navi = UINavigationController(rootViewController: self)
navi.modalPresentationStyle = .fullScreen
navi.transitioningDelegate = pushPopStylePresentDelegate
sourceController.present(navi, animated: true)
// 自定义转场动画---居中弹窗
let pd = AlertPresentationController(show: self, from: sourceController) { ctx in
ctx.usingBlurBelowCoverAnimators(style: .regular)
}
transitioningDelegate = pd
sourceController.present(self, animated: true) {
let _ = pd
}
}
}
使用方法
一、注册
1.1 实现路由源协议JJBlockRouterSource
enum SimpleRouter: String, CaseIterable {
case systemPush = "/app/systemPush"
...
}
extension SimpleRouter: JJBlockRouterSource {
var routerPattern: String {
return rawValue
}
func makeRouterDestination(parameters: [String : String], context: Any?) -> JJBlockRouterDestination {
switch self {
case .systemPush:
return SystemPushController()
...
}
}
}
1.2 调用register方法
SimpleRouter.allCases.forEach { try! $0.register() }
二、跳转
2.1 通过具体的JJBlockRouterSource对象跳转路由
JJBlockRouter.default.open(SimpleRouter.systemPush)(self)
2.2 通过具体的path跳转路由
JJBlockRouter.default.open("/app/systemPush")(nil)
2.3 通过具体的URL跳转路由
if let url = URL(string: "https://www.appwebsite.com/app/systemPush/") {
JJBlockRouter.default.open(url)(self)
}
三、传参数
3.1 通过实现JJBlockRouterSource协议的具体对象传参数
// 注册
enum PassParameterRouter {
case byEnum(p: String, q: Int)
...
}
var routerParameters: [String : String] {
switch self {
case let .byEnum(p: p, q: q):
return ["p": p, "q": "\(q)"]
...
}
}
// A
JJBlockRouter.default.open(PassParameterRouter.byEnum(p: "entry", q: 108))(self)
// 参数: ["p": "entry", "q": 12]
3.2 通过path或者URL传参数
// 注册
enum PassParameterRouter {
case byUrl = "/app/passParameterByUrl/:pid/:name"
...
}
// A
JJBlockRouter.default.open("/app/passParameterByUrl/12/jack")(self)
// 参数: ["pid": "12", "name": "jack"]
3.3 通过path或者URL带query传参数
// 注册
enum PassParameterRouter {
case byUrlWithQuery = "/app/search"
...
}
// A
JJBlockRouter.default.open("/app/search?name=lili&age=18")(self)
// 参数: ["name": "lili", "age": "18"]
3.4 通过context传参数
// 注册
enum PassParameterRouter {
case byContext = "/app/passParameterByContext"
...
}
// A
JJBlockRouter.default.open(PassParameterRouter.byContext, context: 12)(self)
// B
func showDetail(withMatchRouterResult result: JJBlockRouter.MatchResult, from sourceController: UIViewController) {
if let pid = result.context as? Int {
self.pid = pid
}
sourceController.navigationController?.pushViewController(self, animated: true)
}
3.5 混和URL与context传参数
// 注册
enum PassParameterRouter {
case mixUrlAndContext = "/app/mixUrlAndContext/:pid/:text"
...
}
// A
JJBlockRouter.default.open("/app/mixUrlAndContext/12/keke", context: arc4random_uniform(2) == 0)(self)
3.6 将参数用于UIViewController的初始化
// 注册
enum PassParameterRouter {
case parameterForInit = "/app/parameterForInit/:id"
...
}
func makeRouterDestination(parameters: [String : String], context: Any?) -> JJBlockRouterDestination {
switch self {
case .parameterForInit:
let idstr = parameters["id"] ?? ""
let numberFormatter = NumberFormatter()
let id = numberFormatter.number(from: idstr)?.intValue
return PassParametersForInitController(id: id ?? 0)
...
}
}
// A
JJBlockRouter.default.open("/app/parameterForInit/66")(self)
// B
init(id: Int) {
pid = id
super.init(nibName: nil, bundle: nil)
}
四、回调
4.1 正常block回调: A跳转B, B通过路由block将数据回调给A
// A
let router = JJBlockRouter.default.open(BlockRouter.backBlock)(self)
router?.register(blockName: "onSend", callback: { obj in
print("get data: \(obj) from router block")
})
// B
dismiss(animated: true) { [weak self] in
self?.router?.perform(blockName: "onSend", withObject: 5)
}
4.2 非正常block回调: A跳转B, A通过路由block将实时数据回调给B
主要用于,A的数据是实时变化的,B需要拿到A的最新数据
// A
let router = JJBlockRouter.default.open(BlockRouter.frontBlockB)(self)
router?.register(blockName: "onNeedGetNewestData", callback: { [weak self] obj in
guard let self = self,
let block = obj as? (Int) -> () else {
return
}
block(self.data)
})
// B
let block: (Int) -> () = { [weak self] data in
self?.button.setTitle("\(data)", for: [])
}
router?.perform(blockName: "onNeedGetNewestData", withObject: block)
4.3 转发block回调: A需要跳转B,但是条件达不到,需要跳转到其它路由界面C,此时可以正常拿到C的回调
这里A虽然是调用B的路由,但是仍然可以收到C的回调
// register 转发
func register() throws {
try JJBlockRouter.default.register(pattern: routerPattern, mapRouter: { matchResult in
guard case .mapBlock = self else {
return self
}
let needGotoLoginController = arc4random_uniform(2) == 0
if needGotoLoginController { // 需要登录,转发给登录路由
return SimpleRouter.login
}
return self
})
}
// A
let router = JJBlockRouter.default.open(BlockRouter.mapBlock)(self)
router?.register(blockName: "loginSuccess", callback: { _ in
print("登录成功")
})
五、匹配到路由控制器与当前控制器属于同一类时情景
5.1 3种操作
/// 匹配到的路由跟当前展示的界面相同时的操作
public enum MatchedSameRouterDestinationAction {
/// 不做任何操作
case none
/// 更新数据
case update
/// 展示新界面
case new
}
5.2 匹配到相同类的协议方法事例
/// 当匹配到的路由跟当前展示的界面相同时的操作方法,默认返回`new`
///
/// 返回`none`时,不做任何操作
///
/// 返回`update`时,会调用`updateWhenRouterIdentifierIsSame`方法来更新当前界面
///
/// 返回`new`时,会调用`showDetail`来重新展示新的界面
/// - Parameter result: 匹配结果
func actionWhenMatchedRouterDestinationSameToCurrent(withNewMatchRouterResult result: JJBlockRouter.MatchResult) -> JJBlockRouter.MatchedSameRouterDestinationAction {
return .update
}
func updateWhenRouterIdentifierIsSame(withNewMatchRouterResult result: JJBlockRouter.MatchResult) {
pid = parseId(from: result.parameters)
title = "\(pid)"
}
使用需求
-
iOS 11.0+
-
Swift 5+
安装
Cocoapods
use_frameworks!
pod 'JJBlockRouter'