简化TableViewController

不知道你有没有写过这样界面,简单的信息流展示界面,没有复杂的结构,只有一个section,数据来源于同一个接口。写这样的界面有一些固定重复的代码需要写 —— 网络请求、网络相关的界面处理、tableview的代理。针对只有一个section、数据来源单一的界面,可以提取出一个框架,来完成这套固定的流程,减少重复代码

还有一些更基础的代码,比如刷新、加载更多功能的加入、空白页的显示,但刷新、加载更多、空白页的显示属于更大范围的重复代码,不止这篇博文讨论的【单section,数据来源于同一个接口】的界面,更多其他类型的界面也会用到,所以这些更基础的功能就不提及了。

之所以要求单section,是因为多section和单section所需要的数据结构差异比较大,单section的界面通常用一个数组做存储就行了,但多section的就不能只用一个数组做存储;数据来源于同一个接口也是一样的道理,数据处理的简单,上拉刷新的时候是replace操作,下拉刷新的时候是append操作

【单section单一数据来源】界面 加载数据普遍流程:


单section单数据源流程.png

代码实现大概就是下面这样:

struct FlowModel {
}

class InformationFlowController: UIViewController {
    var datas: [FlowModel] = []
    var tableView: UITableView = UITableView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //加载界面
        setupUI()
        
        //加载数据
        loadData()
    }
    
    func loadData() {
        //用自己封装的第三方进行网络请求
        Network.request(api, success: { (data: Data) in
            
            //拿到数据后转换成目标模型
            let models: [FlowModel] = transDataToModels(data)
            
            //存储数据
            self.datas = models
            
            //重载数据
            tableView.reloadData()
        }) { (error) in
        }
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return datas.count
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return HeightOfCellAtIndexPath
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let data = datas[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier, for: indexPath)
        cell.setData(data)
        return cell
    }
}

这样的页面加载数据流程类似,但是流程中会需要情景数据,其中:

  • 网络请求:需要接口地址
  • 解析数据:需要知道model类型用来解析数据
  • tableView.numberOfRow:model数组的count
  • tableView.heightForRow:该index下的model对应cell的高度
  • tableView.cellForRow:需要model对应的cell类型用来取cell,还要根据需要给cell注入数据

把这些情景数据剥离出流程,方案如下:

  • 网络请求:需要接口地址 —— 通过func来获取,具体的子controller通过覆写func来提供接口地址
  • 解析数据:需要知道model类型用来解析数据 —— 使用泛型
  • tableView.numberOfRow:model数组的count —— 内置一个数据存储结构
  • tableView.heightForRow:该index下的model对应cell的高度 —— 数据模型提供高度
  • tableView.cellForRow:需要model对应的cell类型用来取cell,还要根据需要给cell注入数据 —— 数据模型需要提供cell类型,cell需要一个通用的注入数据口

使用泛型解决数据模型不同的问题,是基于现在大部分解析json数据的第三方都是根据类型来进行解析的

把height放到数据中有一个好处,就是当cell不定高时,可以根据数据计算出高度
把cell class放到数据中,可以应对信息流中多种cell类型的情况,根据数据选择cell类型。

根据以上解决方案,整理出一个基类:

class BaseController<Model: IUIInfo>: UIViewController {
    var datas: [Model] = []
    var tableView: UITableView = UITableView()
    
    func getAPI() -> API {
        //override this to provide api
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //加载界面
        setupUI()
        
        //加载数据
        loadData()
    }
    
    func loadData() {
        //用自己封装的第三方进行网络请求
        Network.request(getAPI(), success: { (data: Data) in
            
            //拿到数据后转换成目标模型
            let models: [Model] = transDataToModels(data)
            
            //存储数据
            self.datas = models
            
            //重载数据
            tableView.reloadData()
        }) { (error) in
        }
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return datas.count
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return datas[indexPath.row].height
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let data = datas[indexPath.row]
        let cell = tableView.dequeueReusableCell(withIdentifier: data.cellClass().identifier, for: indexPath)
        (cell as? ICanAssignData)?.setData(delegate: self, indexPath: indexPath, data: data)
        return cell
    }
}

protocol IUIInfo {
    func cellClass() -> UITableViewCell.Type
    var height: CGFloat {get}
}

protocol ICanAssignData {
    func setData(delegate: Any?, indexPath: IndexPath, data: Any) // 传这几个参数是经验之谈
}

struct FlowModel: IUIInfo {
    func cellClass() -> UITableViewCell.Type {
        return FlowModelCell.self
    }
    
    var height: CGFloat {
        return 100
    }
}

class FlowModelCell: UITableViewCell, ICanAssignData {
}

extension UITableViewCell {
    
    static var identifier: String {
        return String(describing: self)
    }
    
}

这样InformationFlowController可以简化成这样

class InformationFlowController: BaseController<FlowModel> {
    override func getAPI() -> API {
        return informationFlowAPI
    }
}

更新流程图如下:


单section单数据源优化后的流程.png

可以根据需要预留数据处理前后的方法,留给具体情景下的子controller处理数据的机会。

数据模型遵循IUIInfo协议,cell遵循ICanAssignData协议,就可以省下 网络请求、网络请求后通用的数据处理、网络请求相关的界面状态、tableview基础协议实现,应该算是一笔划算的买卖

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,128评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,316评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,737评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,283评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,384评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,458评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,467评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,251评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,688评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,980评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,155评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,818评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,492评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,142评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,382评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,020评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,044评论 2 352