iOS 架构组件:让你的 TableView 优雅起来
作者丨indulge_in
https://www.jianshu.com/p/7db73489ad99
GitHub 地址:
https://github.com/indulgeIn/YBHandyTableView
一、传统方式的弊端
UITableView是出场率极高的视图组件,开发者通过实现
然而当某一个界面结构比较复杂且多元的时候,开发者往往需要写大量的if/else/else if或switch分支语句来区分不同section/row的视图类型及其布局,由于UITableView相关协议方法的职责单一性,这种分支语句会重复出现在多个协议方法里面。
显然在这种场景下,UITableView变得不那么优雅。
二、常规优化思路
理所当然的,大家很容易想到使用一个中间类来将协议过于分散的管理方式集中起来:
@interface CellLayout : NSObject
@property (nonatomic, strong) Class cellClass;
@property (nonatomic, assign) CGFloat cellHeight;
@property (nonatomic, strong) AnyModel *cellModel;
...
@end
然后在UITableView相关各个协议方法里从NSArray
这种思路有两点需要注意:
需要一个包含某个 Cell 所有布局信息的中间类
在中间类确定的情况下,
笔者思考过后,花了一天时间做了一个小组件
https://github.com/indulgeIn/YBHandyTableView
它解决的问题是让开发者更轻松、更优雅的使用UITableView,核心操作就是用数组来替代协议方法为UITableView配置数据。当然,这么做有它的局限性,后文再来分析。
三、组件架构设计
经过前面的分析,组件要做的事情有两个,一个是设计一个中间类,一个是封装
核心思路
按照常规的思路,可能会想到设计一个通用的中间类,就像之前说的CellLayout,然后利用继承的特性来为CellLayout添加额外的属性(比如数据model)。这样确实能达到目的,不过这样带来了较为严重的耦合,需要开发者一开始就知道他必须写一个类来继承自你的CellLayout,若本身业务中需要继承另外一个类就很蛋疼了(毕竟 OC 不支持多继承);再者,若某一天想要剔除这种方案可能会很麻烦,CellLayout设计得越臃肿、包含的业务越多将越难剥离。
并且,一个CellLayout是解决不了问题的,因为配置UITableView可能需要UITableViewCell的一些数据,也需要一些通用的方法来告知UITableViewCell何时配置数据刷新UI,也就意味着按照这种逻辑,还需要写一个BaseTableViewCell......
显然,这种方式并不优雅,也违背了依赖倒置原则。
笔者的做法是将这个“中间类”抽象出来,作为两个协议:YBHTCellProtocol和YBHTCellModelProtocol,这两个协议包含了布局UITableView所需的数据,当然可以结合自己的业务扩充这两个协议。YBHTCellProtocol由自定义的UITableViewCell来实现;YBHTCellModelProtocol随意开发者用什么类来实现,通常情况下,使用包含UITableViewCell所需数据的Model来实现是最快捷的做法(可看Demo中的使用案例)。
保证深度定制性
考虑到一个问题,UITableView相关协议方法非常多,若为YBHTCellProtocol和YBHTCellModelProtocol拓展所有的配置将会需要大量的代码,可能有些得不偿失。
所以笔者使用多代理 (YBHandyTableViewProxy) 来保证组件使用方深度定制的需求,也是为了避免某些特殊情况下,使用该组件的业务模块能快速的拓展之前没有的功能:
- (void)ybht_addDelegate:(id< UITableViewDelegate>)delegate;
- (void)ybht_addDataSource:(id< UITableViewDataSource>)dataSource;
当然这样做会有隐患,所以建议读者朋友若想使用该组件先了解它的原理,该组件的代码不多也不高深,相信只要感兴趣的朋友能很快理解。
四、组件的弊端
组件的配置方式很简单:
NSArray> tmpArr = ...;
[anyTableView.ybht_rowArray addObjectsFromArray:tmpArr];
[anyTableView reloadData];
正如代码所见,需要传入的是实现YBHTCellModelProtocol协议的实例,同时需要对应的UITableViewCell实现YBHTCellProtocol协议(可对比 UML 类图)。
取个例子,若你在UIViewController里面写了一个UITableView,然后使用该组件配置数据,可以明确的是组件将
那么,它们如何间接的交互呢?
1.YBHandyTableViewIMP是组件实现
2.从另一个方面思考问题,从组件的使用方法可知,UIViewController和id
3.基于响应链的传递路径来拦截事件。这种方式比较巧,但是却始终感觉不是那么稳妥,它的好处是处理UITableViewCell的交互事件完全可以不经过该组件就能完成。
最后,笔者建议使用第二种方式。不过不管哪种方式来说都不太优雅了,在业务开发中应该多考虑一下,UITableViewCell中会不会有大量的事件需要传递到最外层的业务,比如跳转界面、网络请求等就可以直接在UITableViewCell里面处理。若大量的交互是必然的(或者说是为了满足业务架构规范),那就放弃“偷懒”,专门设计一个适合业务的方式吧。
五、结语
本文是笔者做的一个小实践的思路分享,需要明白的是,一个代码设计并非能满足所有的业务,特别是这种和具体业务紧密相连的组件。在一开始笔者还满怀希望,觉得这个组件的场景很大,后来发现有很多局限性。
组件总是会让粒度变大,当你追求更小粒度的时候你会发现:我去,好像这个组件没有了意义?。
推荐↓↓↓
长
按
关
注
?【16个技术公众号】都在这里!
涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 1 习近平G20里约峰会展现大国担当 7979380
- 2 多国驻乌克兰大使馆因袭击风险关闭 7920724
- 3 78岁老太将减持2.5亿股股票 7889753
- 4 二十国集团里约峰会将会卓有成效 7740490
- 5 俄导弹击中乌水电站大坝 7691804
- 6 孙颖莎王艺迪不敌日本削球组合 7536566
- 7 高三女生酒后被强奸致死?检方回应 7484049
- 8 第一视角记录虎鲨吞下手机全程 7353531
- 9 73岁王石独自带娃被偶遇 7257068
- 10 智慧乌镇点亮数字经济新未来 7174242