前言:
iOS强调模块化、组件化,讲究模块内高内聚,模块间低耦合。
那么模块与模块之间的低耦合,就要求模块间的通信要尽可能的减少依赖,SDRouter,就是这样的中间产物。
SDRouter的设计是参照一水流年的想法来实现的,总体和他的想法保持一致,但是会更加的简洁、易用,作者设计的拓展性强,但是依赖过多,导致库不太容易单独存在。那么,你可以看到使用该库,可以使用URL的方式打开任何Native页面,如下面一个伪协议:asone://oneController?title=as_one
asone
是AppSchema.完全按照http协议涉及的跳转,让该库SDRouter的结构简单,想法也简单。
先设想一下:如何从AController以URL的方式跳转到BController并携带参数?
先看下SDRouter中是怎么实现的:
- (void)viewDidLoad {
[super viewDidLoad];
// 在页面上添加一个button
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 80)];
[button setTitle:@"one" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button addTarget:self action:@selector(go) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
// 点击跳转到下一个控制器 并携带下一个控制器的标题
- (void)go {
NSDictionary *param = @{@"title":@"as_one"};
NSURL *url = SDURLRouteQueryLink(OneController, param);
[[SDRouter shareRutor] rutor:url];
}
再设想一下:如何从BController以OpenUrl的方式打开CController并传递参数?
// 先忽略+load方法中的内容
+ (void)load {
[[SDRouter shareRutor] addPaten:OneController callback:^(SDRouterContext *context) {
NSLog(@"优品财富: %@",context.paramters);
OneViewController *textOneVc = [[OneViewController alloc] init];
textOneVc.navigationItem.title = context.paramters[@"title"];
[context.topNavigationController pushViewController:textOneVc animated:YES];
}];
}
// 先看这里
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor colorWithRed:((float)arc4random_uniform(256) / 255.0) green:((float)arc4random_uniform(256) / 255.0) blue:((float)arc4random_uniform(256) / 255.0) alpha:1.0];
// 页面上添加一个按钮
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 80)];
[button setTitle:@"openUrl" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button addTarget:self action:@selector(openUrl) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
// 点击调转,以openUrl的方式打开一个控制器并携带参数,该参数是navigationTitle。
- (void)openUrl {
NSDictionary *dict = @{@"title":@"as_two"};
NSURL *url = SDURLRouteQueryLink(TwoController, dict);
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:^(BOOL success) {}];
}
最后再想一下,H5怎么快捷的打开CController并携带参数?
// H5页面添加一个按钮并添加js跳转
<input class="class1 class2" type="button" value="跳转到as_Three页面" style="width:500px;height:150px;font-size:40px"onclick=javascrtpt:jump()>
function jump(){
window.location="asone://threeController?title=as😝three";
}
上面三种设想可以参看下图:
SDRouter设计思想:
- URL部分:看似URL跳转,实际上是对URL的解析、组合编解码。
SDRouterUtil主要包含了对URL的解析及组合编解码的方法。
@interface NSString (SDURLEncode)
- (NSString *)URLEncode;
- (NSString *)URLDecode;
@end
// 用来处理参数携带 拼接参数 返回编码、拼接后的URL
FOUNDATION_EXTERN NSURL *SDURLRouteQueryLink(NSString *baseUrl, NSDictionary *query);
// 添加参数
FOUNDATION_EXTERN NSString *SDURLRouteJoinParamterString(NSString *urlStr, NSString *query);
// 将拼接好的参数encode
FOUNDATION_EXTERN NSString *SDURLRouteEncodeURLQueryParamters(NSDictionary *paramter);
// 将参数decode
FOUNDATION_EXTERN NSDictionary *SDURLRouteDecodeURLQueryParamters(NSString *urlStr);
- 跳转:
URL拼接好,从AController跳转到BController,跳转的方法其实很简单。
[[SDRouter shareRutor] rutor:url];
那这句代码又做了什么呢?重点看这段代码
+ (void)load {
[[SDRouter shareRutor] addPaten:OneController callback:^(SDRouterContext *context) {
NSLog(@"优品财富: %@",context.paramters);
OneViewController *textOneVc = [[OneViewController alloc] init];
textOneVc.navigationItem.title = context.paramters[@"title"];
[context.topNavigationController pushViewController:textOneVc animated:YES];
}];
}
在SDRouter中提供了一个注册的方法,必须在load方法中注册,+load是已知的执行最早的方法,比main函数好要早,这里不做过多解释,不明白可以参看你真的了解+load方法吗?
addPaten:OneController
其实这个oneController是定义的字符串常量,如:asone://oneController?title=as_one,在App启动后,SDRouter会记录当前这个控制器和所携带的callback
。
// 实现比较简单
- (void)addPaten:(NSString *)paten callback:(SDCompleteCallback)callback{
NSDictionary *dict = @{paten:callback};
if (![_results containsObject:dict]) {
[_results addObject:dict];
}
}
在调用[[SDRouter shareRutor] rutor:url]
的时候,会匹配需要前往的url。
- (void)rutor:(NSURL *)paten {
SDURLParser *parser = [[SDURLParser alloc] initWithURL:paten];
[_results enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([[obj allKeys].firstObject isEqualToString:parser.paten]) {
SDRouterContext *context = [[SDRouterContext alloc] init];
context.paramters = parser.paramters;
if ([obj allValues].firstObject) {
SDCompleteCallback callback = (SDCompleteCallback)[obj allValues].firstObject;
callback(context);
}
*stop = YES;
}
}];
}
push
到这里,已经能够保证在+load方法中获取到参数,重点是页面怎么push出来。因为+load方法执行时,是不能够获取到navigationController的,那么怎么获取到并push呢?全局UI栈
其实一开始并没有该类,但是通过其他方式获取总是觉的鸡肋,还是通过一水文章的点拨才有了该类。
该类中利用runtime方法替换,重写Controller的生命周期方法,声明一个指针数组,在viewWillApear的时候加入UI栈,在disWill的时候移除UI栈,参数的携带和栈顶控制器的携带,是通过SDContext来完成的。该类中包含了参数及navigation。
讲解比较粗略,想了解的可以到demo中查看。
github