iOS开发之DataSource神奇魔法,优雅的写法让你轻松驾驭TableView

项目地址:https://github.com/cocbin/CBTableViewDataSource

简介

最近在重构之前写的代码的时候,发现基本每个viewController里面都有一段又臭又长的代码用于定义tableView的dataSourcedelegate,于是我在想,有没有更优雅的方式来书写dataSource,于是乎就产生了CBTableViewDataSource。

使用CBTableViewDataSource之前


// define a enum to split section

typedef NS_ENUM(NSInteger, SectionNameDefine) {
    SECTION_ONE,
    SECTION_TWO,
    SECTION_THREE,
    SECTION_FOUR,
    //...
    COUNT_OF_STORE_SECTION
};

// define identifier for section

#define IDENTIFIER_ONE  @"IDENTIFIER_ONE"
#define IDENTIFIER_TWO  @"IDENTIFIER_TWO"
#define IDENTIFIER_THREE  @"IDENTIFIER_THREE"
#define IDENTIFIER_FOUR @"IDENTIFIER_FOUR"
//...

// register cell class for section

[self.tableView registerClass:[OneCell class] forCellWithReuseIdentifier:IDENTIFIER_ONE];
[self.tableView registerClass:[TwoCell class] forCellWithReuseIdentifier:IDENTIFIER_TWO];
[self.tableView registerClass:[ThreeCell class] forCellWithReuseIdentifier:IDENTIFIER_THREE];
[self.tableView registerClass:[FourCell class] forCellWithReuseIdentifier:IDENTIFIER_FOUR];

// implementation datasource protocol

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return COUNT_OF_STORE_SECTION;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return ((NSArray*)self.data[section]).count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSUInteger section = (NSUInteger) indexPath.section;
    NSUInteger index = (NSUInteger) indexPath.row;
    switch(section) {
        case SECTION_ONE:
        // to do something
            return cell;
        case SECTION_TWO:
        // to do something
            return cell;
        case SECTION_THREE:
        // to do something
            return cell;
            
            //...
    }
    
    return cell;
}
// ...

使用CBTableViewDataSource 之后

CBTableViewDataSource * dataSource = CBDataSource(self.tableView)
     .section()
     .cell([OneCell class])
     .data(self.viewModel.oneData)
     .adapter(^UITableViewCell *(OneCell * cell,NSDictionary * data,NSUInteger index){

         //bind data form data to cell
         
         return cell;
     })
     
     .section()
     .cell([TwoCell class])
     .data(self.viewModel.twoData)
     .adapter(^UITableViewCell *(TwoCell * cell,NSDictionary * data,NSUInteger index){

         //bind data form data to cell
         
         return cell;
     })
     // ...
     .make();

CBTableViewDataSource允许我们以函数式的方式定义dataSouce,逻辑顺序和页面的呈现顺序一致。
每个section以section()开头,在section()之后,可以对该section进行一些配置,要求每个section必须设置cell,data,和adapter。cell表示该section使用的cell类,data表示该section的数据,adapter用于将数据和cell绑定起来。同时还能配置section中cell的高度,或者设置自动计算高度。也可是设置section的标题,cell的点击事件等等。

CBTableViewDataSource主要解决了以下几个问题:

  1. 避免了书写各种乱七八糟的宏定义,自动注册cell类,自动设置identifier。
  2. 提供了一套完美解决不同高度cell的计算问题,提供自动计算cell高度的接口。
  3. 提供一套优雅的api,十分优雅并且有逻辑地书写dataSource。

DEMO解读

DEMO包括两个页面,First展示了复杂多section页面时的用法,通过一个仿各种市面上流行的APP的首页,体现了该框架书写dataSource条理清晰,逻辑顺序和页面呈现的顺序完全一致的优点。

IMG_0220.png
IMG_0221.png

second页面通过一个Feed页面,展示了autoHeight的用法。只要调用autoHeight函数,一句话解决cell高度计算问题。

IMG_0222.png

用法

Install

框架一共包括四个文件

CBDataSourceMaker.h
CBDataSourceMaker.m

CBTableViewDataSource.h
CBTableViewDataSource.m

可以直接通过Pod下载使用

pod ....(还没上传)

或者直接将上述四个文件复制到你的项目中即可使用。

Import

#import <CBTableViewDataSource/CBTableViewDataSource.h>

声明

@property(nonatomic, retain) CBTableViewDataSource *  dataSource;

初始化

_dataSource = CBDataSource(self.tableView).section()
      .title(@"section one")
      .cell([TestCell class])
      .data(array)
      .adapter(^(TestCell * cell,NSDictionary * dic,NSUInteger index){
          cell.content.text = dic[@"content"];
          return cell;
      })
      .make()

!!!注意!!!
不能直接为dataSource赋值

//BAD
self.tableView.dataSource = CBDataSource(self.tableView)
    .section()
    .cell(...)
    .data(...)
    .adapter(...)
    .make()

因为UITableView的dataSource声明的是weak,赋值完因为没有任何强引用导致它的内存会被直接释放。

API

CBDataSource(UITableView * tableView)

创建一个CBDataSourceMaker对象,用于创建CBTableViewDataSource,传入一个需要绑定该dataSourcetableView对象

section()

用于分割多个section,每个section的开头到要使用section()声明一个section的开始

cell(Class cell)

传入一个cell的class,如[UITableViewCell class]
表示当前section都使用这个cell,注意,cell不需要注册,框架会自动注册并绑定identifier

data(NSArray * data)

传入一个数组,表示用于呈现在界面上的数据

adapter(****^****(id cell,id data,NSUInteger index))

适配器,使用该方法将数据和cell绑定起来。
参数是一个block,该block会传来一个cell对象,一个data对象,一个index。
可以直接在block上对参数类型进行强制转换。
如:

adapter(^(GoodsCell * cell,GoodsModel * goods,NSUInterger index){
    cell.goods = goods;
    return cell;
})

headerView(UIView*(****^****)())

设置tableHeaderView
参数是一个Block,要求返回一个UIView。

footerView(UIView*(^)())

设置tableFooterView
参数是一个Block,要求返回一个UIView。
常用于取消当页面空白时,tableView呈现多余的下划线。
如:

footerView(^(){
    //返回一个空白View,这样页面没内容时或者内容不足一页,就不会出现多余的线条。
    return [[UIView alloc]init];
})

height(CGFloat * height)

单独为每个section设置一个固定的高度。
********有两个特例:********

  • 当使用了autoHeight之后,该设置失效
  • 当在所有section之前设置height,将为所有section公共的height

autoHeight()

自动计算cell高度,用于cell高度不固定的情况。

注意:

  • 当cell的高度固定时,请不要使用autoHeight,因为autoHeight计算高度会消耗一定性能,尽管该框架已经对高度计算做了非常完美的缓存处理,但是对于高性能的追求一定要做到精益求精。
  • 该设置只对autolayout有效。

一定要正确设置好约束:

  • 所有cell里面的组件一定要放在cell.contentView里面,不然会计算错误
  • 一定要有完整的约束。

确定一个约束是否完整有两个原则

  1. 对于cell内部每个独立的控件,都能确定位置和尺寸,比如左上角定在cell的左上角,然后设置高度宽度确定尺寸,或者设置右下角确定尺寸,前提是右下角相对的组件是能确定位置的。另外,UILabel和UIImageView,这种有内容的控件,只需要确定一个方向的尺寸,就会更具内容自动计算出另一个方向的尺寸,比如label知道宽度,和内容,就能算高度。
  2. 对于cell本身,必须能确定其尺寸。尺寸会通过约束其上下左右的控件来计算,这些所以约束其下和右的控件必须能确定位置和尺寸。值得说的是,这里很容易遗漏掉底部的约束,因为cell就算没有底部约束,也不会报错,但是不能满足计算出cell高度的必要条件。

event(^(NSUInteger index,id data))

参数要求一个Block,用于设置cell的点击事件,index表示点击了当前section的index位置,data表示当前点击位置的数据。

title(NSString* title)

用于设置每个section的标题。

make()

在设置完毕之后执行,表示已经设置完毕了。

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

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,727评论 1 92
  • 1.badgeVaule气泡提示 2.git终端命令方法> pwd查看全部 >cd>ls >之后桌面找到文件夹内容...
    i得深刻方得S阅读 4,628评论 1 9
  • 返回生成器对象 所以程序调用后无输出 生成器对象和迭代器对象类似,都支持可迭代接口,next函数 输出结果 再次调...
    lupinwu阅读 255评论 0 0
  • ■文/从前慢 最近这两年,我得了动不动买书的强迫症,看到或听人说到的自己觉得好的书大多都会买,一次整理书时发现倒有...
    从前慢者阅读 404评论 2 5
  • 一直下着雨,其实也是宣告着春天来临,虽然总是不由自主、日复一日的被各种琐碎淹没和牵绊,但也一直惦记着想去看看树、看...
    sunsmile笑忘书阅读 406评论 1 1