更好的模式,更轻量级的UITableView

更好的模式,更轻量级的UITableView

在软件开发过程中我们经常会运用各类设计模式来使得代码节藕,让代码看起来更整洁,Gof的23种设计模式,值得一看。其他先不谈,先聊聊经常遇到包括去面试的时候面试官会和你聊的MVC等。

Model-View-Controller

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
stop,不想copy了,大家直接看百度吧。😄
MVC百度介绍

对MVC的延伸扩展有MVVM,MVP大家自行百度吧,不论神马设计模式,请记住切忌不要过分依赖和相信他,有的时候回归本我或许能带给大家更好的效果。

文章本天成,妙手偶得之
设计亦如此

大而空的介绍那些不想讲了,大家有没发现在包含UITableView的ViewController的时候,必须去实现一堆protocol。一个字:烦,而且这好多重复的代码,每个UITableView其实都大同小异,有多少个cell,每个cell的样式是怎么样的,然后cell里的内容根据model来显示等等。而且这里面会有个小问题,就是view和model有一个交互。。。必须解耦~!因为在一个大的比较良好的工程里面,很多view是应该能够被重用的而不应该是依赖某些特定的Model。如果把绘制的这部分代码写在viewcontroller里,又会造成一个很烦的问题,一旦数据有所更改,你就得去翻这个viewcontroller,尤其是protocol方法多了后简直是找的有点忧桑~!那么我们就应该对这个UITableView进行一个瘦身计划。

更轻量级的UITableView

首先我们发现一个tableview无非显示的就是一个数组里的数据,每个数组元素对应的是某个model的实例。那么这个datasource应该是可以抽出来的。

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
28
29
30
31
32
33
34
35
/// DataSource 接口类
class ArrayDataSource: NSObject, UITableViewDataSource {
var items: [AnyObject] = []
var cellIdentifier = ""
var configBlock: (AnyObject, AnyObject) -> Void = {_,_ in }
override init() {
super.init()
}
func initWithItems(list: [AnyObject], cellIdentifier: String, configCell: (AnyObject, AnyObject) -> Void ) -> ArrayDataSource {
self.items = list
self.cellIdentifier = cellIdentifier
self.configBlock = configCell
return self
}
func itemAtIndexPath(indexPath: NSIndexPath) -> AnyObject {
return self.items[indexPath.row]
}
// MARK: DataSource delegate
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(self.cellIdentifier, forIndexPath: indexPath)
let item = self.itemAtIndexPath(indexPath)
self.configBlock(cell, item)
return cell
}
}

这个地方就把绘制的数量和cell的样式做了一个抽象。我们再看看具体的UITableView 的datasource的delegate实现的地方。

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
28
29
30
31
32
33
34
35
36
37
var list: [MoneyDataInfo] = []
var arrayDataSource: ArrayDataSource?
let cellStoryboardName = "xxTableViewCell"
/**
初始化tableView
*/
func setupTableView() -> Void {
self.tableView.delegate = self
self.tableView.tableFooterView = UIView()
self.tableView.mj_header = MJRefreshNormalHeader(refreshingTarget: self, refreshingAction: #selector(requestAccountRecordList))
self.tableView.mj_header.beginRefreshing()
let nib = UINib(nibName: cellStoryboardName, bundle: NSBundle.mainBundle())
self.tableView.registerNib(nib, forCellReuseIdentifier: cellStoryboardName)
// Adapter
self.setupAdapter()
}
/**
构建适配器
*/
func setupAdapter() {
// Adapter
self.arrayDataSource = ArrayDataSource()
self.arrayDataSource?.initWithItems(self.list, cellIdentifier: cellStoryboardName, configCell: { (cell: AnyObject, data: AnyObject) in
let tmpCell = cell as! MoneyInfoCellTableViewCell
let tmpData = data as! MoneyDataInfo
tmpCell.configForData(tmpData)
})
self.tableView.dataSource = self.arrayDataSource
}

这样子,只需要在数据发生变化的时候调用一下self.setupAdapter()这个方法即可。

那么就到了具体cell的绘制的地方。how to do it?
我们应该把model传入到cell里去然后逐步绘制么,可以这样子,但并不科学,我们应该好好利用分类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension MoneyInfoCellTableViewCell {
/**
根据数据填充
- parameter data: MoneyDataInfo
*/
func configForData(data: MoneyDataInfo) -> Void {
self.selectionStyle = .None
...
// 这个地方根据data来进行对应的绘制
}
}

为什么这么做,这样子文件写的更多了,但有明显的几个好处,这里的tableviewcell可以重复利用,不耦合具体的数据,当数据有修改的时候,只需要修改这个分类的这个config方法即可,无需再去把viewcontroller折腾一遍,代码更加的清晰,条理性更好!这里抛弃掉了一些大而空的东西,只着力于解决一些具体的事务。
无论什么所谓权威的东西都不要过分迷信和执着,都要去尝试它并自己去作出合理的判断。还是用那句话作结尾:
文章本天成,妙手偶得之
设计亦如此

参考文献:
objc.cn https://objccn.io/issue-1-1/