iOS 架构组件:让你的 TableView 优雅起来

作者 : 开心源码 本文共3363个字,预计阅读时间需要9分钟 发布时间: 2022-05-12 共231人阅读

GitHub 地址:YBHandyTableView

一、传统方式的弊端

UITableView是出场率极高的视图组件,开发者通过实现<UITableViewDataSource><UITableViewDelegate>协议方法来配置布局逻辑,面向协议设计模式在苹果的代码设计中很常见,它能适应大部分的业务场景且足够灵活。这种方式优点很多,比方某一时刻组件只要要关心当前需要的数据,避免了多余的计算,同时也可以让数据及时释放减小内存峰值。UITableView相关的协议方法充分表现了单一职责准则,比方一个协议方法返回 Cell 的高度,一个协议方法返回 Cell 的实例。

然而当某一个界面结构比较复杂且多元的时候,开发者往往需要写大量的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<CellLayout *> layoutArray数组中拿到数据配置就行了,如此,开发者只要要关心如何构建layoutArray数组,避免了写过多的分支语句。

这种思路有两点需要注意:

  • 需要一个包含某个 Cell 所有布局信息的中间类
  • 在中间类确定的情况下,<UITableViewDataSource><UITableViewDelegate>协议方法里面的逻辑就已经可以共用了。

笔者思考过后,花了一天时间做了一个小组件,它处理的问题是让开发者更轻松、更优雅的使用UITableView,核心操作就是用数组来替代协议方法为UITableView配置数据。当然,这么做有它的局限性,后文再来分析。

三、组件架构设计

YBHandyTableView UML类图

经过前面的分析,组件要做的事情有两个,一个是设计一个中间类,一个是封装<UITableViewDataSource><UITableViewDelegate>协议方法的实现。

核心思路

按照常规的思路,可能会想到设计一个通用的中间类,就像之前说的CellLayout,而后利用继承的特性来为CellLayout增加额外的属性(比方数据model)。这样的确能达到目的,不过这样带来了较为严重的耦合,需要开发者一开始就知道他必需写一个类来继承自你的CellLayout,若本身业务中需要继承另外一个类就很蛋疼了(毕竟 OC 不支持多继承);再者,若某一天想要剔除这种方案可能会很麻烦,CellLayout设计得越臃肿、包含的业务越多将越难剥离。

并且,一个CellLayout是处理不了问题的,由于配置UITableView可能需要UITableViewCell的少量数据,也需要少量通用的方法来告知UITableViewCell何时配置数据刷新UI,也就意味着按照这种逻辑,还需要写一个BaseTableViewCell……

显然,这种方式并不优雅,也违反了依赖倒置准则。

笔者的做法是将这个“中间类”笼统出来,作为两个协议:YBHTCellProtocolYBHTCellModelProtocol,这两个协议包含了布局UITableView所需的数据,当然可以结合自己的业务扩充这两个协议。YBHTCellProtocol由自己设置的UITableViewCell来实现;YBHTCellModelProtocol随便开发者用什么类来实现,通常情况下,使用包含UITableViewCell所需数据的Model来实现是最快捷的做法(可看Demo中的使用案例)。

保证深度定制性

考虑到一个问题,UITableView相关协议方法非常多,若为YBHTCellProtocolYBHTCellModelProtocol拓展所有的配置将会需要大量的代码,可能有些得不偿失。

所以笔者使用多代理商 (YBHandyTableViewProxy) 来保证组件使用方深度定制的需求,也是为了避免某些特殊情况下,使用该组件的业务板块能快速的拓展之前没有的功能:

- (void)ybht_addDelegate:(id<UITableViewDelegate>)delegate;- (void)ybht_addDataSource:(id<UITableViewDataSource>)dataSource;

当然这样做会有隐患,所以建议读者朋友若想使用该组件先理解它的原理,该组件的代码不多也不高深,相信只需感兴趣的朋友能很快了解。

四、组件的弊端

组件的配置方式很简单:

NSArray<id<YBHTCellModelProtocol>> tmpArr = ...;[anyTableView.ybht_rowArray addObjectsFromArray:tmpArr];[anyTableView reloadData];

正如代码所见,需要传入的是实现YBHTCellModelProtocol协议的实例,同时需要对应的UITableViewCell实现YBHTCellProtocol协议(可比照 UML 类图)。

取个例子,若你在UIViewController里面写了一个UITableView,而后使用该组件配置数据,可以明确的是组件将<UITableViewDataSource><UITableViewDelegate>协议封装起来,UIViewController和你定制的那些UITableViewCell已经没有了耦合,也就意味着,它们之间的交互将不能直接进行。

那么,它们如何间接的交互呢?

  1. YBHandyTableViewIMP是组件实现<UITableViewDataSource><UITableViewDelegate>协议的类,那么将UIViewController对象传入到该类就能实现与UITableViewCell的交互,但是因为YBHandyTableViewIMPUITableViewCell不直接依赖而是都依赖于YBHTCellProtocol协议,这为定制性的交互带来了困难。
  2. 从另一个方面思考问题,从组件的使用方法可知,UIViewControllerid<YBHTCellModelProtocol>之间是有关联的,而id<YBHTCellModelProtocol>UITableViewCell<YBHTCellProtocol>是有关联的,所以可以通过id<YBHTCellModelProtocol>UIViewController传递到UITableViewCell中,而后进行交互。
  3. 基于响应链的传递路径来阻拦事件。这种方式比较巧,但是却始终感觉不是那么稳妥,它的好处是解决UITableViewCell的交互事件完全可以不经过该组件就能完成。

最后,笔者建议使用第二种方式。不过不论哪种方式来说都不太优雅了,在业务开发中应该多考虑一下,UITableViewCell中会不会有大量的事件需要传递到最外层的业务,比方跳转界面、网络请求等即可以直接在UITableViewCell里面解决。若大量的交互是必然的(或者者说是为了满足业务架构规范),那就放弃“偷懒”,专门设计一个适合业务的方式吧。

五、结语

本文是笔者做的一个小实践的思路分享,需要明白的是,一个代码设计并非能满足所有的业务,特别是这种和具体业务紧密相连的组件。在一开始笔者还满怀希望,觉得这个组件的场景很大,后来发现有很多局限性。

组件总是会让粒度变大,当你追求更小粒度的时候你会发现:我去,如同这个组件没有了意义??。

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » iOS 架构组件:让你的 TableView 优雅起来

发表回复