項(xiàng)目地址:https://github.com/cocbin/CBTableViewDataSource
簡(jiǎn)介
最近在重構(gòu)之前寫的代碼的時(shí)候,發(fā)現(xiàn)基本每個(gè)viewController里面都有一段又臭又長(zhǎng)的代碼用于定義tableView的dataSource和delegate,于是我在想,有沒有更優(yōu)雅的方式來書寫dataSource,于是乎就產(chǎn)生了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允許我們以函數(shù)式的方式定義dataSouce,邏輯順序和頁面的呈現(xiàn)順序一致。
每個(gè)section以section()開頭,在section()之后,可以對(duì)該section進(jìn)行一些配置,要求每個(gè)section必須設(shè)置cell,data,和adapter。cell表示該section使用的cell類,data表示該section的數(shù)據(jù),adapter用于將數(shù)據(jù)和cell綁定起來。同時(shí)還能配置section中cell的高度,或者設(shè)置自動(dòng)計(jì)算高度。也可是設(shè)置section的標(biāo)題,cell的點(diǎn)擊事件等等。
CBTableViewDataSource主要解決了以下幾個(gè)問題:
- 避免了書寫各種亂七八糟的宏定義,自動(dòng)注冊(cè)cell類,自動(dòng)設(shè)置identifier。
- 提供了一套完美解決不同高度cell的計(jì)算問題,提供自動(dòng)計(jì)算cell高度的接口。
- 提供一套優(yōu)雅的api,十分優(yōu)雅并且有邏輯地書寫dataSource。
DEMO解讀
DEMO包括兩個(gè)頁面,First展示了復(fù)雜多section頁面時(shí)的用法,通過一個(gè)仿各種市面上流行的APP的首頁,體現(xiàn)了該框架書寫dataSource條理清晰,邏輯順序和頁面呈現(xiàn)的順序完全一致的優(yōu)點(diǎn)。


second頁面通過一個(gè)Feed頁面,展示了autoHeight的用法。只要調(diào)用autoHeight函數(shù),一句話解決cell高度計(jì)算問題。

用法
Install
框架一共包括四個(gè)文件
CBDataSourceMaker.h
CBDataSourceMaker.m
CBTableViewDataSource.h
CBTableViewDataSource.m
可以直接通過Pod下載使用
pod ....(還沒上傳)
或者直接將上述四個(gè)文件復(fù)制到你的項(xiàng)目中即可使用。
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()
因?yàn)閁ITableView的dataSource聲明的是weak,賦值完因?yàn)闆]有任何強(qiáng)引用導(dǎo)致它的內(nèi)存會(huì)被直接釋放。
API
CBDataSource(UITableView * tableView)
創(chuàng)建一個(gè)CBDataSourceMaker對(duì)象,用于創(chuàng)建CBTableViewDataSource,傳入一個(gè)需要綁定該dataSource的tableView對(duì)象
section()
用于分割多個(gè)section,每個(gè)section的開頭到要使用section()聲明一個(gè)section的開始
cell(Class cell)
傳入一個(gè)cell的class,如[UITableViewCell class]。
表示當(dāng)前section都使用這個(gè)cell,注意,cell不需要注冊(cè),框架會(huì)自動(dòng)注冊(cè)并綁定identifier
data(NSArray * data)
傳入一個(gè)數(shù)組,表示用于呈現(xiàn)在界面上的數(shù)據(jù)
adapter(****^****(id cell,id data,NSUInteger index))
適配器,使用該方法將數(shù)據(jù)和cell綁定起來。
參數(shù)是一個(gè)block,該block會(huì)傳來一個(gè)cell對(duì)象,一個(gè)data對(duì)象,一個(gè)index。
可以直接在block上對(duì)參數(shù)類型進(jìn)行強(qiáng)制轉(zhuǎn)換。
如:
adapter(^(GoodsCell * cell,GoodsModel * goods,NSUInterger index){
cell.goods = goods;
return cell;
})
headerView(UIView*(****^****)())
設(shè)置tableHeaderView
參數(shù)是一個(gè)Block,要求返回一個(gè)UIView。
footerView(UIView*(^)())
設(shè)置tableFooterView
參數(shù)是一個(gè)Block,要求返回一個(gè)UIView。
常用于取消當(dāng)頁面空白時(shí),tableView呈現(xiàn)多余的下劃線。
如:
footerView(^(){
//返回一個(gè)空白View,這樣頁面沒內(nèi)容時(shí)或者內(nèi)容不足一頁,就不會(huì)出現(xiàn)多余的線條。
return [[UIView alloc]init];
})
height(CGFloat * height)
單獨(dú)為每個(gè)section設(shè)置一個(gè)固定的高度。
********有兩個(gè)特例:********
- 當(dāng)使用了autoHeight之后,該設(shè)置失效
- 當(dāng)在所有section之前設(shè)置height,將為所有section公共的height
autoHeight()
自動(dòng)計(jì)算cell高度,用于cell高度不固定的情況。
注意:
- 當(dāng)cell的高度固定時(shí),請(qǐng)不要使用autoHeight,因?yàn)閍utoHeight計(jì)算高度會(huì)消耗一定性能,盡管該框架已經(jīng)對(duì)高度計(jì)算做了非常完美的緩存處理,但是對(duì)于高性能的追求一定要做到精益求精。
- 該設(shè)置只對(duì)autolayout有效。
一定要正確設(shè)置好約束:
- 所有cell里面的組件一定要放在cell.contentView里面,不然會(huì)計(jì)算錯(cuò)誤
- 一定要有完整的約束。
確定一個(gè)約束是否完整有兩個(gè)原則
- 對(duì)于cell內(nèi)部每個(gè)獨(dú)立的控件,都能確定位置和尺寸,比如左上角定在cell的左上角,然后設(shè)置高度寬度確定尺寸,或者設(shè)置右下角確定尺寸,前提是右下角相對(duì)的組件是能確定位置的。另外,UILabel和UIImageView,這種有內(nèi)容的控件,只需要確定一個(gè)方向的尺寸,就會(huì)更具內(nèi)容自動(dòng)計(jì)算出另一個(gè)方向的尺寸,比如label知道寬度,和內(nèi)容,就能算高度。
- 對(duì)于cell本身,必須能確定其尺寸。尺寸會(huì)通過約束其上下左右的控件來計(jì)算,這些所以約束其下和右的控件必須能確定位置和尺寸。值得說的是,這里很容易遺漏掉底部的約束,因?yàn)閏ell就算沒有底部約束,也不會(huì)報(bào)錯(cuò),但是不能滿足計(jì)算出cell高度的必要條件。
event(^(NSUInteger index,id data))
參數(shù)要求一個(gè)Block,用于設(shè)置cell的點(diǎn)擊事件,index表示點(diǎn)擊了當(dāng)前section的index位置,data表示當(dāng)前點(diǎn)擊位置的數(shù)據(jù)。
title(NSString* title)
用于設(shè)置每個(gè)section的標(biāo)題。
make()
在設(shè)置完畢之后執(zhí)行,表示已經(jīng)設(shè)置完畢了。