最近在重構以前寫的代碼的時候,發現基本每一個viewController裏面都有一段又臭又長的代碼用於定義tableView的dataSource
和delegate
,因而我在想,有沒有更優雅的方式來書寫dataSource
,因而乎就產生了CBTableViewDataSource。git
項目地址:https://github.com/cocbin/CBTableViewDataSourcegithub
使用CBTableViewDataSource以前api
// 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主要解決了如下幾個問題:框架
避免了書寫各類亂七八糟的宏定義,自動註冊cell類,自動設置identifier。ide
提供了一套完美解決不一樣高度cell的計算問題,提供自動計算cell高度的接口。函數
提供一套優雅的api,十分優雅而且有邏輯地書寫dataSource。性能
DEMO包括兩個頁面,First展現了複雜多section頁面時的用法,經過一個仿各類市面上流行的APP的首頁,體現了該框架書寫dataSource條理清晰,邏輯順序和頁面呈現的順序徹底一致的優勢。
atom
second頁面經過一個Feed頁面,展現了autoHeight的用法。只要調用autoHeight
函數,一句話解決cell高度計算問題。
框架一共包括四個文件
CBDataSourceMaker.h CBDataSourceMaker.m CBTableViewDataSource.h CBTableViewDataSource.m
能夠直接經過Pod下載使用
pod 'CBTableViewDataSource', '~> 1.0.0'
或者直接將上述四個文件複製到你的項目中便可使用。
#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,賦值完由於沒有任何強引用致使它的內存會被直接釋放。
建立一個CBDataSourceMaker
對象,用於建立CBTableViewDataSource
,傳入一個須要綁定該dataSource
的tableView
對象
用於分割多個section,每一個section的開頭到要使用section()聲明一個section的開始
傳入一個cell的class,如[UITableViewCell class]
。
表示當前section都使用這個cell,注意,cell不須要註冊,框架會自動註冊並綁定identifier
傳入一個數組,表示用於呈如今界面上的數據
^
(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; })
^
)())設置tableHeaderView
參數是一個Block,要求返回一個UIView。
^
)())設置tableFooterView
參數是一個Block,要求返回一個UIView。
經常使用於取消當頁面空白時,tableView呈現多餘的下劃線。
如:
footerView(^(){ //返回一個空白View,這樣頁面沒內容時或者內容不足一頁,就不會出現多餘的線條。 return [[UIView alloc]init]; })
單獨爲每一個section設置一個固定的高度。
有兩個特例:
當使用了autoHeight以後,該設置失效
當在全部section以前設置height,將爲全部section公共的height
自動計算cell高度,用於cell高度不固定的狀況。
注意:
當cell的高度固定時,請不要使用autoHeight,由於autoHeight計算高度會消耗必定性能,儘管該框架已經對高度計算作了很是完美的緩存處理,可是對於高性能的追求必定要作到精益求精。
該設置只對autolayout有效。
必定要正確設置好約束:
全部cell裏面的組件必定要放在cell.contentView裏面,否則會計算錯誤
必定要有完整的約束。
肯定一個約束是否完整有兩個原則
對於cell內部每一個獨立的控件,都能肯定位置和尺寸,好比左上角定在cell的左上角,而後設置高度寬度肯定尺寸,或者設置右下角肯定尺寸,前提是右下角相對的組件是能肯定位置的。另外,UILabel和UIImageView,這種有內容的控件,只須要肯定一個方向的尺寸,就會更具內容自動計算出另外一個方向的尺寸,好比label知道寬度,和內容,就能算高度。
對於cell自己,必須能肯定其尺寸。尺寸會經過約束其上下左右的控件來計算,這些因此約束其下和右的控件必須能肯定位置和尺寸。值得說的是,這裏很容易遺漏掉底部的約束,由於cell就算沒有底部約束,也不會報錯,可是不能知足計算出cell高度的必要條件。
^
(NSUInteger index,id data))參數要求一個Block,用於設置cell的點擊事件,index表示點擊了當前section的index位置,data表示當前點擊位置的數據。
用於設置每一個section的標題。
在設置完畢以後執行,表示已經設置完畢了。