初学ios时,界面之间的跳转都是使用的原生push,但是随着app的复杂性变大,使用的过程中发现 每次显示同一个界面的时候,创建方式基本相同,而且稍微疏忽有可能会遗漏某个显示界面需要的配置属性。后来就想着界面跳转这些逻辑其实是可以单独封装起来的,然后再网上又看到了 路由跳转的概念,然后尝试去学习。
一、MGJRouter
之前最开始尝试使用路由跳转的时候考虑了很多 最后使用了MGJRouter,当时还是Objective-C项目。MGJRouter使用中也有很多问题。
比如所有的url需要进入项目后先注册,每次调用的时候因为是url是字符串 每次都要检查url是否写错,中间如果url写错,调试起来也挺麻烦,后来想想 这种库只适合用来出来外链,内部跳转使用这个纯属找罪受。
二、RouterManager (SwifterRouter)
后来学习了Swift, 逐渐的学习如何优雅开发,当时为了能够更好的实现路由跳转,就封装了一个RouterManager。整体思路就是通过RouteUrlType跳转。
RouteUrlType是一个协议,协议里面只需要实现一个方法来返回一个控制器。
RouterManager内部跳转时,先通过动态获取当前最上层UINavigationController的方式获取currentNavC,然后根据RouteUrlType创建的控制器,进行跳转。
具体使用时,因为项目中分了很多个模块,比如首页,个人中心等,我把每个模块对应了一个枚举(当然也可以使用其他方式比如类,只要遵循RouteUrlType协议就行),比如


如果界面有回调就用闭包的形式,,不过用闭包回调的方式有时候会出现回调地狱的情况出现, 如

当然这种回调地狱使用PromiseKit,也能很优雅的解决掉。
这种方式总体来说,就是把跳转时如何创建界面,封装并归类了起来,相比之前的三方库MGJRouter之类的形式,只是不用注册url,并且使用的时候不用再手写url字符串调用。而且如果涉及到外链跳转界面,也可以在这个基础上,先解析url,然后根据对应url调转界面
这种界面跳转方式,从我一开始写swift时就开始用,用着很简单也很简便,但是也会出现一些问题,特别是界面跳转流程比较复杂的时候,虽然都能处理 但是总感觉少了点什么,处理起来很麻烦不是很优雅。
三、Coordinator & Aspect
1.我发现我每次项目大的变动时都是看到了其他的项目的新思路,这次因为要做一个区块链钱包,就看到了一个github上开源的钱包项目,里面用到了Coordinator概念。Coordinator本身主要不是用来处理界面跳转流程的,但是不妨碍,我在此基础上的一些扩展。
原先我的项目中,基本上处理类似于点击一个button显示另一个界面,有可能是push或者present,这些逻辑都是在控制器或者view中实现的,无非是跳转的那一刻使用RouterManager。
看到Coordinator这个在我眼中很新奇的东西后,不断地上网搜索概念,和引入我的项目中的可行性,中途又看到了Redux的新思路,然后经过差不多将近10天的时间,不断地改进删减,才把项目整体框架定型(还好新项目本身刚开始还不算大,只有几十个界面)。
之前界面回调逻辑更喜欢用闭包的形式,这个主要跟原先写Objective-C用协议没感受到协议的魅力有关,现在界面回调主要用的是代理。
2.其中界面跳转部分,是定义了一个CanPushProtocol的协议,这个协议只有一个navCon属性,只要遵循了这个协议,这个Coordinator就有了push的能力,然后每个界面 在控制器的上面又抽离了一层NavItemCoordinator,每个NavItemCoordinator已经遵循了RouteUrlType拥有可被跳转的能力。
每个NavItemCoordinator(界面协调器)内部强引用了一个NavItemViewController(界面控制器),Controller本身不持有Coordinator,需要交互的时候则通过代理。
NavItemCoordinator里面处理了如何初始化一个界面,还有类似于点击一个button如何显示下一个界面,下一个界面显示结束通过代理回调时如何处理。
这些逻辑被NavItemCoordinator抽离后,NavItemViewController就能最大化的被复用。NavItemViewController界面里面的标题或者界面显示的数据,都可以在NavItemCoordinator中配置好后,加载到NavItemViewController里面。完全可以实现多种NavItemCoordinator加载同一种NavItemViewController,实现NavItemViewController的复用。
3.因为界面是通过Coordinator跳转的,每次显示一个界面都需要初始化一个Coordinator,很多界面都有回调逻辑,使用代理每次都要设置,这些如何创建Coordinator,还有界面回调的逻辑,我又在Coordinator上面封装了一层Aspect。
Aspect也是一个Coordinator,之所以叫Aspect,是因为我也没有想好叫啥名字,每个NavItemCoordinator 都有一个Aspect代理,Aspect里面包含了如何创建Coordinator,界面回调逻辑。
比如一个设置界面,任何界面想要跳转我这个设置界面都有需要遵循SettingAspect协议,遵循协议后,就具备了跳转设置界面的能力,根据提示实现好设置界面的回调逻辑,然后调用push(setting())跳转。这个setting()方法是Aspect协议已经默认实现好的,返回了一个SettingCoordinator。
Aspect就像一个胶水一样,遵循了对应的Aspect协议,就拥有了和对应界面沟通的能力。
Aspect本身是可以随意组装的,比如登录注册逻辑一般是不分家的,完全可以定义一个HotAccountJumpAspect,来处理热钱包的登录注册逻辑,当然与之对应的还有冷钱包的导入和创建逻辑。可以随意堆砌。

4.做了Aspect这一层后,单单是考虑界面跳转的话,我就在想Coordinator这一层有没有必要去掉,仔细想想,还是留着比较好,不说其他,光是控制器复用,逻辑和视图代码分离,这些好处我都要留着Coordinator,更何况从进入app后几乎所有的逻辑代码都可以放在Coordinator这一层里面。在原先MVC或者MVVM后面又多了一层Coordinator



四、总结
总体来说,做了这么多封装,不为别的,就为了简单,好用,可控。随着app的复杂度增加,如果不做改变使用原来的逻辑也能写,无非是处理一些复杂的流程时,需要消耗的脑细胞更多一点。做了这些封装,就算遇到复杂的逻辑,也能轻松应对,任意揉捏。
使用Coordinator&Aspect,使用起来简单,也有点不好就是创建一个界面需要创建的东西太多了,需要一个Aspect协议,一个ViewController控制器,一个Coordinator类。但是这个缺点完全可以通过XCode模板文件的方式解决。相对来说,我更倾向于清晰明了 各司其职的写法。避免不了要根据 对应逻辑创建对应类型 的事情。
五、RxFlow
看到这个项目的时候突然 灵光一闪,我的项目中的界面跳转能不能也通过Rx来处理下,而且原来的Aspect这个名字有了更好的名字Step或者Flow。虽然不用RxFlow项目,但是把我的项目中的界面跳转转换成信号,感觉会完美很多。好吧,11月,真是事多的一个月,这个月看来闲不下来了。
六、一直没空看RxFlow,今天深入研究下,发现里面很多概念很清晰,Flow,Step,Presentable等等,准备使用RxFlow再改下项目。
网友评论