iOS開發之DataSource神奇魔法,優雅的寫法讓你輕鬆駕馭TableView

簡介

最近在重構以前寫的代碼的時候,發現基本每一個viewController裏面都有一段又臭又長的代碼用於定義tableView的dataSourcedelegate,因而我在想,有沒有更優雅的方式來書寫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主要解決了如下幾個問題:框架

  1. 避免了書寫各類亂七八糟的宏定義,自動註冊cell類,自動設置identifier。ide

  2. 提供了一套完美解決不一樣高度cell的計算問題,提供自動計算cell高度的接口。函數

  3. 提供一套優雅的api,十分優雅而且有邏輯地書寫dataSource。性能

DEMO解讀

DEMO包括兩個頁面,First展現了複雜多section頁面時的用法,經過一個仿各類市面上流行的APP的首頁,體現了該框架書寫dataSource條理清晰,邏輯順序和頁面呈現的順序徹底一致的優勢。
clipboard.png
clipboard.pngatom

second頁面經過一個Feed頁面,展現了autoHeight的用法。只要調用autoHeight函數,一句話解決cell高度計算問題。

clipboard.png

用法

Install

框架一共包括四個文件

CBDataSourceMaker.h
CBDataSourceMaker.m

CBTableViewDataSource.h
CBTableViewDataSource.m

能夠直接經過Pod下載使用

pod 'CBTableViewDataSource', '~> 1.0.0'

或者直接將上述四個文件複製到你的項目中便可使用。

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()

在設置完畢以後執行,表示已經設置完畢了。

相關文章
相關標籤/搜索