IOS 代码优化之 整洁的UITableView

UITableView(以下简称TableView)是IOS应用中非常通用的组件,很多界面都可以使用TableView直接实现。但是我见过很多朋友实现TableView的DataSource和Delegate的时候,实现方法里面写了非常复杂,特别是cellForRowAtIndexPath方法,把很多Cel相关的代码也写到DataSource实现方法里面,特别是多种Cell风格的时候 ,那代码更是不敢恭维。一旦想要增加一种Cell类型,又要更改那个方法。代码维护性,可读性和健壮性都产生了很多负面影响。本文中,将为你展示一个tableView相关代码的整洁和组织技术。

1、本文整体思路

Cell完全由Model控制,cellForRowAtIndexPath方法只是做获取Cell和通知Cell刷新

2、增加基本Model

考虑到获取Cell的时候需要通过一个String类型的Identifier,所以我们将该Identifier保存在具体的Model里面,这样不同风格的Cell通过控制Model的Identifier就可以实现。而一般情况下一种Cell类型就对应一个Identifier;又考虑到每一种Cell对应一个Class类名也是唯一的。所以我们就将Cell的类名作为其Identifier。一般情况下Model具体实例一旦决定,对应的Cell也就决定了,所以我们就设计出了一个基本Model,代码如下:

/** 基本Model类 */
class BaseModel: NSObject {

    /** 该Model对象对应的Cell的Identifier */
    let cellIdentifier: String

    /** 通过Cell类名初始化(关联Cell) */
    init(cellClass: AnyClass){
        self.cellIdentifier = NSStringFromClass(cellClass)
    }
    
}

3、增加基本Cell

考虑到可以为Cell进行统一控制,比如点击效果啥的,我们也为Cell创建一个基本类,但是现在没有任何实现,代码如下:

/** 基本Cell类 */
class BaseCell: UITableViewCell {
    
}

4、为UITableView添加新的注册Cell方法

因为统一了Cell的Identifier,所以我们为UITableView添加新的注册Cell的方法(通过Cell类类型注册Cell),代码如下:

extension UITableView {

    /** 注册通过xib创建的Cell */
    func ex_registerNib(nibName:String, cellClass:AnyClass){
        let cellNib = UINib(nibName: nibName,bundle: nil)
        self.registerNib(cellNib, forCellReuseIdentifier: NSStringFromClass(cellClass))
    }

    /** 通过类名注册Cell */
    func ex_registerCell(cellClass:AnyClass){
        self.registerClass(cellClass, forCellReuseIdentifier: NSStringFromClass(cellClass))
    }
}

5、事例

接下去我就用一个事例展示如何使用这些封装:

5.1、事例Model

class Model: BaseModel {
    
    var number: Int
    /** Cell点击回调,通过这种方式将Cell中的一些事件,比如按钮点击等回调到对应位置 */
    var callback: ((model: Model) -> Void)?
    
    init(cellClass: AnyClass, number: Int) {
        self.number = number
        super.init(cellClass: cellClass)
    }
    /** cell中文本的显示控制 */
    var text: String {
        return String(format: "我的Number是:%d", number)
    }
    /** cell高度控制(如果自适应高度则不需要此项) */
    var height: CGFloat {
        return 48
    }
}

5.2、事例Cell

class Cell: BaseCell {
    
    private var titleLabel = UILabel()
    override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        // 初始化Cell控件
        self.contentView.addSubview(titleLabel)
        titleLabel.frame = CGRectMake(20, 10, 200, 30)
        titleLabel.textColor = UIColor.redColor()
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    /** cell刷新代码 */
    func refreshCell(model: Model) {
        titleLabel.text = model.text
    }
    
}

5.3、事例ViewController,包含大多数情况下DataSource和Delegate实现

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    private var tableView: UITableView = UITableView()
    private var models: [Model] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.ex_registerCell(Cell.self)
        tableView.dataSource = self
        tableView.delegate = self
        
        self.view.addSubview(tableView)
        tableView.frame = UIScreen.mainScreen().bounds
        // 添加测试数据
        models += (0..<10).map{ index in
            let model = Model.init(cellClass: Cell.self, number: index)
            model.callback = { data in print("click \(data.text)") } // 指定这个Model对应Cell点击事件
            return model
        }
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return models.count
    }
    func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
        return models[indexPath.row].height
    }
    
    func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
        return 0.001
    }
    
    func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        return 0.001
    }
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let model = self.models[indexPath.row]
        let cell = tableView.dequeueReusableCellWithIdentifier(model.cellIdentifier, forIndexPath: indexPath) as! Cell
        cell.refreshCell(model) // 关联Model和Cell
        return cell
    }
    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        let data = self.models[indexPath.row]
        data.callback?(model: data)
    }
}

6、总结

这样设计之后,DataSource和Delegate代码都变得很简洁,Cell完全有Model控制,如果需要增加一种Cell,只要调整Model使得它也能刷新新的Cell(也可以通过增加Model子类实现),然后在Model初始的时候将新的Cell关联一下就行了。
这种方式也适用于UITableView的HeaderView和FooFooterView的控制和UICollectionView



你可能感兴趣的:(IOS-Swift)