iOS開發(fā)之DataSource神奇魔法,優(yōu)雅的寫法讓你輕松駕馭TableView

項(xiàng)目地址:https://github.com/cocbin/CBTableViewDataSource

簡(jiǎn)介

最近在重構(gòu)之前寫的代碼的時(shí)候,發(fā)現(xiàn)基本每個(gè)viewController里面都有一段又臭又長(zhǎng)的代碼用于定義tableView的dataSourcedelegate,于是我在想,有沒有更優(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è)問題:

  1. 避免了書寫各種亂七八糟的宏定義,自動(dòng)注冊(cè)cell類,自動(dòng)設(shè)置identifier。
  2. 提供了一套完美解決不同高度cell的計(jì)算問題,提供自動(dòng)計(jì)算cell高度的接口。
  3. 提供一套優(yōu)雅的api,十分優(yōu)雅并且有邏輯地書寫dataSource。

DEMO解讀

DEMO包括兩個(gè)頁面,First展示了復(fù)雜多section頁面時(shí)的用法,通過一個(gè)仿各種市面上流行的APP的首頁,體現(xiàn)了該框架書寫dataSource條理清晰,邏輯順序和頁面呈現(xiàn)的順序完全一致的優(yōu)點(diǎn)。

IMG_0220.png
IMG_0221.png

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

IMG_0222.png

用法

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è)需要綁定該dataSourcetableView對(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è)原則

  1. 對(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)容,就能算高度。
  2. 對(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è)置完畢了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容