前言
UITableView/UICollectionView
是我们开发中使用最为频繁的两个控件。关于其使用的实践网上已经有很多优秀的总结了,所以我不打算再啰嗦了。今天要讨论的问题基于 objc.io 的一遍文章 Lighter View Controllers,此文讲述如何通过抽取频繁出现的配置类型的代码到专门的一个 DataSource/Delegate
里面来为 Controller
瘦身。我们从中受到了启发,由于文中给出的 demo 不具有通用性,所以打算写一个比较全面的封装来组织 DataSource/Delegate 的代码。
我们先看一下平时都是怎么使用 UITableView
的,一般我们需要做这样几件事:
- 注册需要使用的
cell
的样式到UITableView
- 实现
UITableViewDataSource
的几个方法来告诉UITableView
什么地方怎么如何显示cell
- 实现
UITableViewDelegate
来告诉UITableView
具体每个cell
的高度,以及处理点击事件等
一般情况大致做的就这些。通常的做法就是直接设置当前 controller
为 tableView
的数据源和事件回调委托。这样会造成很多 controller
有大致一致的代码。经验告诉我们,大面积出现类似的代码就可以考虑把这些代码抽取出来封装成一个更加通用的组织形式了。也就是我们要做的事情,其实就是对 UITableViewDataSource
和 UITableViewDelegate
的进一步拆分和封装。
思考一下我们看到的 tableView
都包含哪些部分,展示了哪些元素。从 Apple 提供的 API 中我们可以看出大致包含 tableViewHeader/tableViewFooter
,SectionHeaderView/SectionFooterView
,SectionHeaderTitle/SectionFooterTitle
,sectionIndex
以及最重要的 Cell
。如果我们把这些东西都映射成为一个数据类型,然后直接让 tableView
去取对应的部分数据然后渲染到界面上不就好了么,每个页面我们就不再关心如何去实现 UITableViewDataSource/UITableViewDelegate
,只需要告知必要的信息,其余重复性极高的事情就交给封装的代码来做了,就像在配置界面一样,真正实现「你们做 iOS 的不就是把服务端的数据显示在界面上就好了么」。
废话了这么多,直接上我们的解决方案吧!源码已经放到 GitHub 上了。下面主要说一下怎么用。
代码组织
代码主要分为以下几部分:
TCDataSourceProtocol
: 对UITableView
和UICollectionView
按照界面划分为几个配置不同界面的模块,实现者根据需求实现各自的协议,来 “配置” 界面。TCDataSource
:DataSource
的基类,所有UITableView
和UICollectionView
的数据源的基类,其中已经默认实现了重复率高的代码,其实就是对UITableViewDataSource/UICollectionViewDataSource
的实现。还实现了UITableview
的Move/Edit
操作的辅助方法。UICollectionView
的Move
操作辅助方法等。TCDelegate
:Delegate
的基类,所有UITableView
和UICollectionView
的委托的基类,其中实现了与UIScrollView
相关的一部分功能,比如Cell
的图片懒加载。为子类实现一些辅助方法,比如基于Autolayout
自动计算Cell/SectionHeaderView/SectionFooterView
行高的辅助方法。TCSectionDataMetric
:UITableView/UICollectionView
各个分组的组件的数据封装。包含SectionHeaderView/SectionFooterView
,SectionHeaderTitle/SectionFooterTitle
以及Cell
等的数据。TCGlobalDataMetric
:对整个UITableView/UICollectionView
各个组件的数据的封装。其作为一个容器,里面包含若干个TCSectionDataMetric
。
基本使用
下面直接以我工作的通用样板来说明如何使用,一个场景的文件目录大致像这样:
ProductViewController
(基于UITableView
)ProductViewModel
(采用RAC
来处理网络层逻辑)ProductDataSource
ProductDelegate
Views
Model
基于这样的架构,Controller
文件代码一般保持在 200 到 300 行之间,其他文件行数则更少。这样一来代码清晰了,逻辑自然也比较容易厘清,修改功能也容易多了。至于维护那种打开文件一看就是上千行代码的情况,我的内心是崩溃的。
言归正传,来看一下相关类中的关键代码是怎样的?
ProductViewController
中,初始化 DataSource
和 Delegate
并关联到 tableView
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
lazy var dataSource: ProductDataSource = { ProductDataSource(tableView: self.tableView) }() lazy var delegate: ProductDelegate = { ProductDelegate(tableView: self.tableView) }() lazy var tableView: UITableView = { let tableView = UITableView(frame: CGRectZero, style: .Plain) ... return tableView }() lazy var viewModel: ProductViewModel = { ProductViewModel() }() override func viewDidLoad() { super.viewDidLoad() tableView.delegate = delegate tableView.dataSource = dataSource } internal func methodTakeParamters<T, U>(paramterOne: T, paramterTwo: U) { navigationController.showViewController(vc, sender: self) } |
ProductDataSource
需要继承自 TCDataSource
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
final class ShopSettingDataSource: TCDataSource { } /// 配置能够显示基本的 cell 需要的信息 extension ShopSettingDataSource: TCDataSourceable { /// 注册 Cell 样式到 tableView func registerReusableCell() { tableView?.registerClass(Cell1.self, forCellReuseIdentifier: Cell1.reuseIdentifier) tableView?.registerClass(Cell2.self, forCellReuseIdentifier: Cell2.reuseIdentifier) ... } /// 返回每个位置对应的 Cell 的重用标识符 func reusableCellIdentifierForIndexPath(indexPath: NSIndexPath) -> String { /// 可以通过 globalDataMetric.dataForItemAtIndexPath(indexPath) /// 拿到具体每个 cell 对应的数据,然后通过数据类型来决定使用哪种类型的 cell return reuseIdentifier } /// 为 Cell 配置数据 func loadData(data: TCDataType, forReusableCell cell: TCCellType) { let reusableCell = cell as! UITableViewCell reusableCell.setupData(data) } } |
ProductDelegate
源码大致如下:
1 2 3 4 5 κ的问题基于 objc.io 的一遍文章 Lighter View Controllers,此文讲述如何通过抽取频繁出现的配置类型的代码到专门的一个 DataSource/Delegate 里面来为 Controller 瘦身。我们从中受到了启发,由于文中给出的 demo 不具有通用性,所以打算写一个比较全面的封装来组织 DataSource/Delegate 的代码。
我们先看一下平时都是怎么使用
一般情况大致做的就这些。通常的做法就是直接设置当前 思考一下我们看到的 废话了这么多,直接上我们的解决方案吧!源码已经放到 GitHub 上了。下面主要说一下怎么用。 代码组织代码主要分为以下几部分:
基本使用下面直接以我工作的通用样板来说明如何使用,一个场景的文件目录大致像这样:
基于这样的架构, 言归正传,来看一下相关类中的关键代码是怎样的?
|