iOS页面之间的跳转经常使用基于URL的router和mediator,过去有蘑菇街的方案 ([limboy.me/tech/2016/0…]) 和Casa Taloyum的方案 ([casatwy.com/iOS-Moduliz…]) 的激烈讨论,业界的各类方案其实也是这两种方案的变种,包括豆瓣的解决方案 ([github.com/douban/FRDI…]) 是基于URL注册的扩展,吸纳了Android的设计,解决了URL字符串不能传递对象的缺点。
我认为一个好的页面跳转方案必须考虑到如下功能:html
不一样页面之间直接持有viewcontroller对象而后进行push显然是很差的实践,A页面的改动会影响到持有它的B页面的代码,因此经过一个中间者进行解耦是天然而然的设计。这个中间者能够是一个mediator也能够是一个协议名称。
把页面的class名字做为协议名称是bad case的设计,有时候会看到这样的设计:git
+ (UIViewController *)getControllerWithName:(NSString *)vcClassString action:(NSString *)actionName passObject:(id)object {
// return viewController;
}复制代码
之因此不是好的设计,是由于A页面都感知B页面的类名了,就破坏了解耦的意图,若是B页面想重命名,还须要通知各个引用到B页面名字的人一一更名字。github
A页面打开B页面,A页面常常须要向B页面传递参数,B页面也有可能向A页面回传参数。参数必须支持原始数据类型、字符串还有不方便序列化的对象(如UIImage)。安全
支持远程跳转到指定的页面是router必不可少的功能。因此router要有把url解析成相应页面的能力。对于hybrid架构的H5页面,router也要提供根据url跳转到H5页面的功能。架构
对于支付等涉及到安全性的页面,不该该开放远程跳转的能力。因此router要区分出远程调转和本地跳转,并且对于远程跳转的页面要限制跳转的目标页面范围。ui
提供跳转到栈底的某个页面的功能,由于交互和产品的需求,不少页面并非直接pop出堆栈,而是用户点击后要跳转到栈底的某个特定页面,因此这时候就要求router不能仅仅提供pop的功能,而是须要利用中间者进行跳转,而跳转方案一样要符合解耦的要求,不能直接引用class或者classname。url
过去的一年多经历了几个App,总结下实践下来以为work得还不错的方案。spa
这是配置的页面的scheme,scheme字段用于标识一个页面,在内部跳转到远程跳转都须要用到,统一了内部和外部的跳转。needLogin表示这个页面在push的时候须要验证登陆,若是没有登陆的话就自动唤起登陆页面。allowRemote标识了这个页面容许远程跳转。
对于外部跳转,接口设计以下,url只须要传入定义好协议的字符串便可。设计
[DRCRouter openURL:url];复制代码
对于内部跳转,接口设计以下,params传入dictionary,支持非序列化的对象。code
[DRCRouter pushWithSchema:@"orderDetail" params:@{@"orderId":orderId}];复制代码
其实外部跳转只是内部跳转的一个子集,对于经过openURL打开native页面的状况,最终也是通过解析调用pushWithSchema方法。
对于须要回退到栈底的某个页面的时候,调用接口:
[DRCRouter popToSchema:@"orderDetail"];复制代码
能够调转到栈底的任意页面,原理也是经过配置表去查找scheme对应的vc。
为了安全性的需求,有些页面只能容许内部跳转,外部跳转即便拼对了url也不容许跳转。因此router内部会根据来源来判断是否能跳转到相应的页面。而且,对于须要登陆才能进入的页面,会先弹出登陆框用户登陆后再进入页面。
未完待续。