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

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

相关文章
相关标签/搜索