打造轻量级 ViewController 之抽离 DataSource/Delegate

448 查看

前言

UITableView/UICollectionView 是我们开发中使用最为频繁的两个控件。关于其使用的实践网上已经有很多优秀的总结了,所以我不打算再啰嗦了。今天要讨论的问题基于 objc.io 的一遍文章 Lighter View Controllers,此文讲述如何通过抽取频繁出现的配置类型的代码到专门的一个 DataSource/Delegate 里面来为 Controller 瘦身。我们从中受到了启发,由于文中给出的 demo 不具有通用性,所以打算写一个比较全面的封装来组织 DataSource/Delegate 的代码。

我们先看一下平时都是怎么使用 UITableView 的,一般我们需要做这样几件事:

  • 注册需要使用的 cell 的样式到 UITableView
  • 实现 UITableViewDataSource 的几个方法来告诉 UITableView 什么地方怎么如何显示 cell
  • 实现 UITableViewDelegate 来告诉 UITableView 具体每个 cell 的高度,以及处理点击事件等

一般情况大致做的就这些。通常的做法就是直接设置当前 controllertableView 的数据源和事件回调委托。这样会造成很多 controller 有大致一致的代码。经验告诉我们,大面积出现类似的代码就可以考虑把这些代码抽取出来封装成一个更加通用的组织形式了。也就是我们要做的事情,其实就是对 UITableViewDataSourceUITableViewDelegate 的进一步拆分和封装。

思考一下我们看到的 tableView 都包含哪些部分,展示了哪些元素。从 Apple 提供的 API 中我们可以看出大致包含 tableViewHeader/tableViewFooterSectionHeaderView/SectionFooterViewSectionHeaderTitle/SectionFooterTitlesectionIndex 以及最重要的 Cell。如果我们把这些东西都映射成为一个数据类型,然后直接让 tableView 去取对应的部分数据然后渲染到界面上不就好了么,每个页面我们就不再关心如何去实现 UITableViewDataSource/UITableViewDelegate ,只需要告知必要的信息,其余重复性极高的事情就交给封装的代码来做了,就像在配置界面一样,真正实现「你们做 iOS 的不就是把服务端的数据显示在界面上就好了么」。

废话了这么多,直接上我们的解决方案吧!源码已经放到 GitHub 上了。下面主要说一下怎么用。

代码组织

代码主要分为以下几部分:

  • TCDataSourceProtocol: 对 UITableViewUICollectionView 按照界面划分为几个配置不同界面的模块,实现者根据需求实现各自的协议,来 “配置” 界面。
  • TCDataSourceDataSource 的基类,所有 UITableViewUICollectionView 的数据源的基类,其中已经默认实现了重复率高的代码,其实就是对 UITableViewDataSource/UICollectionViewDataSource 的实现。还实现了 UITableviewMove/Edit 操作的辅助方法。UICollectionViewMove 操作辅助方法等。
  • TCDelegateDelegate 的基类,所有 UITableViewUICollectionView 的委托的基类,其中实现了与 UIScrollView 相关的一部分功能,比如 Cell的图片懒加载。为子类实现一些辅助方法,比如基于 Autolayout 自动计算 Cell/SectionHeaderView/SectionFooterView 行高的辅助方法。
  • TCSectionDataMetricUITableView/UICollectionView 各个分组的组件的数据封装。包含 SectionHeaderView/SectionFooterView, SectionHeaderTitle/SectionFooterTitle 以及 Cell 等的数据。
  • TCGlobalDataMetric:对整个 UITableView/UICollectionView 各个组件的数据的封装。其作为一个容器,里面包含若干个 TCSectionDataMetric

基本使用

下面直接以我工作的通用样板来说明如何使用,一个场景的文件目录大致像这样:

  • ProductViewController(基于 UITableView)
  • ProductViewModel(采用 RAC 来处理网络层逻辑)
  • ProductDataSource
  • ProductDelegate
  • Views
  • Model

基于这样的架构,Controller 文件代码一般保持在 200 到 300 行之间,其他文件行数则更少。这样一来代码清晰了,逻辑自然也比较容易厘清,修改功能也容易多了。至于维护那种打开文件一看就是上千行代码的情况,我的内心是崩溃的。

言归正传,来看一下相关类中的关键代码是怎样的?

ProductViewController 中,初始化 DataSourceDelegate 并关联到 tableView

ProductDataSource 需要继承自 TCDataSource

ProductDelegate 源码大致如下: