iOS开发技巧--如何快速完成TableView布局及代理设置

项目地址:https://github.com/bref-Chan/CCTableDataSource

前言

  • 在iOS开发中,程序猿接触最多的系统控件非UITableView莫属,但是每次使用UITableView的时候都要写一大堆的Delegate和DataSource实现,让视图控制器非常的臃肿,而且业务代码分布在几个方法中,可读性也不好.

  • 所以为了给控制器减负,我们可以将UITableView的代理和数据源实现独立出来,同时实现一个中间类来作为视图控制器与代理类和数据源类的桥梁,将表需要显示的信息放入这个中间类中进行管理

所以,我们需要实现3个类,分别是
CCTableDataItem <负责搭起视图控制器和代理类和数据源类的桥梁>
CCTableViewDelegate
CCTableViewDataSource

将数据显示的行为归于视图(解耦)

在实现上面3个类之前,我们还需要做一些准备工作,我们的目的是给视图控制器减负,所以Cell,headerView,footerView高度的判断和内容的显示这些行为,应该交给视图自己控制,所以我们先创建3个基类,分别对应UITableViewCell,UITableHeaderView,UITableFooterView

@interface CCBaseTableViewCell : UITableViewCell

@property (nonatomic, assign) id delegate;

/**
 必须实现,根据数据返回视图高度
 */
+ (CGFloat)cellHeightForData:(id)data;

/*
 必须实现,将数据显示在cell中
 **/
- (void)bindData:(id)data;
@end
@interface CCBaseTableHeaderView : UIView

@property (nonatomic, weak) id delegate;

/**
 必须实现,根据数据返回视图高度
 */
+ (CGFloat)headerViewHeightForData:(id)data;

/*
 必须实现,将数据显示在header中
 **/
- (void)bindData:(id)data;
@end

@interface CCBaseTableFooterView : UIView

@property (nonatomic, weak) id delegate;

/**
 必须实现,根据数据返回视图高度
 */
+ (CGFloat)footerViewHeightForData:(id)data;

/*
 必须实现,将数据显示在footer中
 **/
- (void)bindData:(id)data;

@end

通过注释大家应该可以知道2个方法的作用了吧,第一个用来返回高度,第二个方法用来显示数据,这样就将业务解耦,分拆一部分到了视图那里

视图控制器和代理类和数据源类的桥梁--CCTableDataItem

工欲善其事必先利其器,在实现Delegate和DataSource通用类之前,我们先创建他们和视图控制器之间的桥梁

我们之前要想对UITableView进行设置,我们必须要先在

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

这个方法中告诉表我们每个区的单元格个数
然后再在

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;  

中告诉表我们有多少个区
最后实现

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

告诉表我们想要的单元格是什么,上面应该显示什么内容.

说实话,我觉得这种方式虽然耦合度是下来了.但业务代码导出分布,导致开发和调试的时候需要在几个方法中跳来跳去,浪费了时间,所以在CCTableDataItem中,我们实现了2个模型,CCTableSectionDataItem和CCTableCellDataItem,我们把区的信息告诉CCTableSectionDataItem,再把单元格的信息告诉CCTableCellDataItem,然后由CCTableDataItem这个类把这些粗活都做了.

现在先来看下CCTableSectionDataItem和CCTableCellDataItem这两个模型

@interface CCTableSectionDataItem : NSObject

//区头视图类型
@property (nonatomic, strong) Class headerClass;
//区尾视图类型
@property (nonatomic, strong) Class footerClass;

//区头视图类型(由xib创建)
@property (nonatomic, strong) Class headerNibClass;
//区尾视图类型(由xib创建)
@property (nonatomic, strong) Class footerNibClass;

//区头视图所对应的数据(将对应数据显示在区头视图)
@property (nonatomic, strong) id sectionHeaderData;
//区尾视图所对应的数据(将对应数据显示在区尾)
@property (nonatomic, strong) id sectionFooterData;

//区头代理方法的实现者(如果区头有代理方法且被实现)
@property (nonatomic, assign) id sectionHeaderDelegate;
//区尾代理方法的实现者(如果区头有代理方法且被实现)
@property (nonatomic, assign) id sectionFooterDelegate;

//这个区拥有的cell
@property (nonatomic, strong) NSMutableArray *cells;

@end
@interface CCTableCellDataItem : NSObject

//Cell的类型
@property (nonatomic, strong) NSString *cellClassName;
//Cell所对应的数据(将数据显示在Cell中)
@property (nonatomic, strong) id cellData;
//Cell的代理方法实现者(如果cell有代理方法且被实现)
@property (nonatomic, assign) id cellDelegate;

@end

各个属性的作用,注释已经写的很清楚了,我们写表的时候无非是加个区头,然后在区头显示一些数据,加个区尾,在区尾显示一些数据,在区头区尾中间加一些单元格,显示一些数据,现在我们就把这些信息封装了一下,交给CCTableDataItem类来管理,下面我们来看一下CCTableDataItem类又有哪些属性

@interface CCTableDataItem : NSObject

@property (nonatomic, strong) id firstData;
@property (nonatomic, strong) id lastData;

@property (nonatomic, strong) NSMutableArray *items;
@end

可以看到CCTableDataItem有一个items属性,这里面装的全都是CCTableSectionDataItem实例所保存的区头区尾的信息,而每个CCTableSectionDataItem都有一个cells属性,里面装的全是CCTableCellDataItem实例保存的单元格的信息,这样,我们就将我们想要表显示的内容都封装起来,交给了CCTableDataItem,剩下来的事情就都交给CCTableDataItem它来做了.现在我们来看看它能做哪些事情

/**
 设置区头视图类型以及相关数据
 
 @param headerClass 区头类型
 @param headerItem  区头相关数据
 */
- (void)addHeaderClass:(Class)headerClass headerDataItem:(id)headerItem;
/**
 设置区头Nib视图类型以及相关数据
 
 @param headerNibClass 区头Nib类型
 @param headerItem  区头相关数据
 */
- (void)addHeaderNibClass:(Class)headerNibClass headerDataItem:(id)headerItem;
/**
 设置区头视图类型和相关数据以及代理对象

 @param headerClass 区头类型
 @param headerItem 区头相关数据
 @param headerDelegate 代理对象
 */
- (void)addHeaderClass:(Class)headerClass headerDataItem:(id)headerItem headerDelegate:(id)headerDelegate;
/**
 设置区头Nib视图类型和相关数据以及代理对象
 
 @param headerNibClass 区头Nib类型
 @param headerItem 区头相关数据
 @param headerDelegate 代理对象
 */
- (void)addHeaderNibClass:(Class)headerNibClass headerDataItem:(id)headerItem headerDelegate:(id)headerDelegate;

/**
 分别设置区头区尾视图类型和相关数据

 @param headerClass 区头类型
 @param headerItem 区头相关数据
 @param footerClass 区尾类型
 @param footerItem 区尾相关类型
 */
- (void)addHeaderClass:(Class)headerClass headerDataItem:(id)headerItem footerClass:(Class)footerClass footerDataItem:(id)footerItem;
/**
 分别设置区头区尾Nib视图类型和相关数据
 
 @param headerNibClass 区头Nib类型
 @param headerItem 区头相关数据
 @param footerNibClass 区尾Nib类型
 @param footerItem 区尾相关类型
 */
- (void)addHeaderNibClass:(Class)headerNibClass headerDataItem:(id)headerItem footerNibClass:(Class)footerNibClass footerDataItem:(id)footerItem;
/**
 分别设置区头区尾视图类型和相关数据以及代理对象
 
 @param headerClass 区头类型
 @param headerItem 区头相关数据
 @param footerClass 区尾类型
 @param footerItem 区尾相关数据
 @param headerDelegate 区头代理
 @param footerDelegate 区尾代理
 */
- (void)addHeaderClass:(Class)headerClass headerDataItem:(id)headerItem headerDelegate:(id)headerDelegate footerClass:(Class)footerClass footerDataItem:(id)footerItem footerDelegate:(id)footerDelegate;
/**
 分别设置区头区尾Nib视图类型和相关数据以及代理对象
 
 @param headerNibClass 区头Nib类型
 @param headerItem 区头相关数据
 @param footerNibClass 区尾Nib类型
 @param footerItem 区尾相关数据
 @param headerDelegate 区头代理
 @param footerDelegate 区尾代理
 */
- (void)addHeaderNibClass:(Class)headerNibClass headerDataItem:(id)headerItem headerDelegate:(id)headerDelegate footerNibClass:(Class)footerNibClass footerDataItem:(id)footerItem footerDelegate:(id)footerDelegate;
/**
 添加Cell类型和相关数据进当前区
 
 @param cellClass Cell类型
 @param dataItem 相关数据,与Cell类中的bindData:方法接收的参数类型一致
 */
- (void)addCellClass:(Class)cellClass dataItem:(id)dataItem;
/**
 添加Cell类型和相关数据以及代理对象进当前区
 
 @param cellClass Cell类型
 @param dataItem 相关数据,与Cell类中的bindData:方法接收的参数类型一致
 @param delegate 代理对象
 */
- (void)addCellClass:(Class)cellClass dataItem:(id)dataItem delegate:(id)delegate;
/**
 将相关数据数组中的数据分别与Cell类型添加进当前区
 
 @param cellClass Cell类型
 @param dataItems 相关数据数组
 */
- (void)addCellClass:(Class)cellClass dataItems:(NSArray *)dataItems;
/**
 讲相关数据数组中的数据分别与Cell类型和代理对象添加进当前区
 
 @param cellClass Cell类型
 @param dataItems 相关数据数组
 @param delegate 代理对象
 */
- (void)addCellClass:(Class)cellClass dataItems:(NSArray *)dataItems delegate:(id)delegate;
/**
 清除数据源
 */
- (void)clearData;
/**
 通过indexPath获取相关数据源
 
 @param indexPath 数据源索引,与Cell索引一致
 @return 相关数据源
 */
- (id)cellDataForIndexPath:(NSIndexPath *)indexPath;
/**
 通过indexPath获取相关Cell类型
 
 @param indexPath Cell索引
 @return 相关Cell类型
 */
- (NSString *)cellClassNameForIndexPath:(NSIndexPath *)indexPath;
/**
 通过indexPath获取相关Cell高度
 
 @param indexPath Cell索引
 @return 相关Cell高度
 */
- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath;
/**
 通过section获取区头高度
 
 @param section 区索引
 @return 相关区头高度
 */
- (CGFloat)headerHeightForSection:(NSInteger)section;
/**
 通过section获取区尾高度
 
 @param section 区索引
 @return 相关区尾高度
 */
- (CGFloat)footerHeightForSection:(NSInteger)section;
/**
 通过区索引获取相关自定义区头数据
 
 @param section 区索引
 @return 相关自定义区头尾数据
 */
- (id)sectionHeaderDataItemForSection:(NSInteger)section;
/**
 通过区索引获取相关自定义区尾数据
 
 @param section 区索引
 @return 相关自定义区头尾数据
 */
- (id)sectionFooterDataItemForSection:(NSInteger)section;

方法看似很多其实都很简单,看一下就能知道他能做的事情无非是把表的信息存入和把已存入的信息拿出来,下面是CCTableDataItem的实现

- (id)firstData
{
    if (self.items && self.items.count > 0) {
        CCTableSectionDataItem *sectionData = [self.items objectAtIndex:0];
        if (sectionData && sectionData.cells && sectionData.cells.count > 0) {
            CCTableCellDataItem *cellData = [sectionData.cells firstObject];
            return cellData.cellData;
        }
    }
    return nil;
}
- (id)lastData
{
    if (self.items && self.items.count > 0) {
        CCTableSectionDataItem *sectionData = [self.items objectAtIndex:self.items.count - 1];
        if (sectionData && sectionData.cells && sectionData.cells.count > 0) {
            CCTableCellDataItem *cellData = [sectionData.cells lastObject];
            return cellData.cellData;
        }
    }
    return nil;
}
- (void)addHeaderClass:(Class)headerClass headerDataItem:(id)headerItem
{
    [self addHeaderClass:headerClass headerDataItem:headerItem headerDelegate:nil footerClass:nil footerDataItem:nil footerDelegate:nil];
}
- (void)addHeaderClass:(Class)headerClass headerDataItem:(id)headerItem headerDelegate:(id)headerDelegate
{
    [self addHeaderClass:headerClass headerDataItem:headerItem headerDelegate:headerDelegate footerClass:nil footerDataItem:nil footerDelegate:nil];
}
- (void)addHeaderClass:(Class)headerClass headerDataItem:(id)headerItem footerClass:(Class)footerClass footerDataItem:(id)footerItem
{
    [self addHeaderClass:headerClass headerDataItem:headerItem headerDelegate:nil footerClass:footerClass footerDataItem:footerItem footerDelegate:nil];
}
- (void)addHeaderClass:(Class)headerClass headerDataItem:(id)headerItem headerDelegate:(id)headerDelegate footerClass:(Class)footerClass footerDataItem:(id)footerItem footerDelegate:(id)footerDelegate
{
    if (self.items) {
        CCTableSectionDataItem *sectionSource = [[CCTableSectionDataItem alloc] init];
        
        sectionSource.headerClass = headerClass;
        sectionSource.footerClass = footerClass;
        sectionSource.sectionHeaderData = headerItem;
        sectionSource.sectionFooterData = footerItem;
        sectionSource.sectionHeaderDelegate = headerDelegate;
        sectionSource.sectionFooterDelegate = footerDelegate;
        
        [self.items addObject:sectionSource];
    }
}
- (void)addHeaderNibClass:(Class)headerNibClass headerDataItem:(id)headerItem
{
    [self addHeaderNibClass:headerNibClass headerDataItem:headerItem headerDelegate:nil footerNibClass:nil footerDataItem:nil footerDelegate:nil];
}
- (void)addHeaderNibClass:(Class)headerNibClass headerDataItem:(id)headerItem headerDelegate:(id)headerDelegate
{
    [self addHeaderNibClass:headerNibClass headerDataItem:headerItem headerDelegate:headerDelegate footerNibClass:nil footerDataItem:nil footerDelegate:nil];
}
- (void)addHeaderNibClass:(Class)headerNibClass headerDataItem:(id)headerItem footerNibClass:(Class)footerNibClass footerDataItem:(id)footerItem
{
    [self addHeaderNibClass:headerNibClass headerDataItem:headerItem headerDelegate:nil footerNibClass:footerNibClass footerDataItem:footerItem footerDelegate:nil];
}
- (void)addHeaderNibClass:(Class)headerNibClass headerDataItem:(id)headerItem headerDelegate:(id)headerDelegate footerNibClass:(Class)footerNibClass footerDataItem:(id)footerItem footerDelegate:(id)footerDelegate
{
    if (self.items) {
        CCTableSectionDataItem *sectionSource = [[CCTableSectionDataItem alloc] init];
        
        sectionSource.headerNibClass = headerNibClass;
        sectionSource.footerNibClass = footerNibClass;
        sectionSource.sectionHeaderData = headerItem;
        sectionSource.sectionFooterData = footerItem;
        sectionSource.sectionHeaderDelegate = headerDelegate;
        sectionSource.sectionFooterDelegate = footerDelegate;
        
        [self.items addObject:sectionSource];
    }
}
- (void)addCellClass:(Class)cellClass dataItem:(id)dataItem
{
    [self addCellClass:cellClass dataItems:@[dataItem] delegate:nil];
}
- (void)addCellClass:(Class)cellClass dataItem:(id)dataItem delegate:(id)delegate
{
    [self addCellClass:cellClass dataItems:@[dataItem] delegate:delegate];
}
- (void)addCellClass:(Class)cellClass dataItems:(NSArray *)dataItems
{
    [self addCellClass:cellClass dataItems:dataItems delegate:nil];
}
- (void)addCellClass:(Class)cellClass dataItems:(NSArray *)dataItems delegate:(id)delegate
{
    CCTableSectionDataItem *sectionSource = [self.items lastObject];
    
    if (self.items && !sectionSource) {
        sectionSource = [[CCTableSectionDataItem alloc] init];
        [self.items addObject:sectionSource];
    }
    
    if (sectionSource) {
        NSMutableArray *sectionCells = sectionSource.cells;
        
        if (!sectionCells) {
            sectionCells = [NSMutableArray array];
            sectionSource.cells = sectionCells;
        }
        
        if (dataItems) {
            for (id dataItem in dataItems) {
                CCTableCellDataItem *cellItem = [[CCTableCellDataItem alloc] init];
                
                cellItem.cellClassName = NSStringFromClass(cellClass);
                cellItem.cellData = dataItem;
                cellItem.cellDelegate = delegate;
                
                [sectionCells addObject:cellItem];
            }
        } else {
            CCTableCellDataItem *cellSource = [[CCTableCellDataItem alloc] init];
            
            cellSource.cellClassName = NSStringFromClass(cellClass);
            cellSource.cellDelegate = delegate;
            
            [sectionCells addObject:cellSource];
        }
    }
}
- (void)clearData
{
    if (self.items) {
        [self.items removeAllObjects];
    }
}
- (id)cellDataForIndexPath:(NSIndexPath *)indexPath
{
    CCTableSectionDataItem *sectionItem = self.items[indexPath.section];
    CCTableCellDataItem *cellItem = sectionItem.cells[indexPath.row];
    
    return cellItem.cellData;
}
- (NSString *)cellClassNameForIndexPath:(NSIndexPath *)indexPath
{
    CCTableSectionDataItem *sectionItem = self.items[indexPath.section];
    CCTableCellDataItem *cellItem = sectionItem.cells[indexPath.row];
    
    return cellItem.cellClassName;
}

- (CGFloat)cellHeightForIndexPath:(NSIndexPath *)indexPath
{
    CCTableSectionDataItem *sectionItem = self.items[indexPath.section];
    CCTableCellDataItem *cellItem = sectionItem.cells[indexPath.row];
    
    Class cellClass = NSClassFromString(cellItem.cellClassName);
    
    if ([cellClass isSubclassOfClass:[CCBaseTableViewCell class]]) {
        return [cellClass cellHeightForData:cellItem.cellData];
    }
    
    return 44.0f;
}
- (CGFloat)headerHeightForSection:(NSInteger)section
{
    CCTableSectionDataItem *sectionItem = self.items[section];
    
    Class headerClass = sectionItem.headerClass;
    
    if ([headerClass isSubclassOfClass:[CCBaseTableHeaderView class]]) {
        return [headerClass headerViewHeightForData:sectionItem.sectionHeaderData];
    }
    return 20.0f;
}
- (CGFloat)footerHeightForSection:(NSInteger)section
{
    CCTableSectionDataItem *sectionItem = self.items[section];
    
    Class footerClass = sectionItem.footerClass;
    
    if ([footerClass isSubclassOfClass:[CCBaseTableFooterView class]]) {
        return [footerClass footerViewHeightForData:sectionItem.sectionHeaderData];
    }
    return 20.0f;
}
- (id)sectionHeaderDataItemForSection:(NSInteger)section
{
    CCTableSectionDataItem *sectionItem = self.items[section];
    return sectionItem.sectionHeaderData;
}
- (id)sectionFooterDataItemForSection:(NSInteger)section
{
    CCTableSectionDataItem *sectionItem = self.items[section];
    return sectionItem.sectionFooterData;
}

这样的代码,先别急,后面会讲到的.这样,我们就把表的Delegate与DataSource和视图控制器之间的桥梁搭好了,接下来就开始实现DataSource吧

UITableViewDataSource的通用实现--CCTableViewDataSource

在UITableView中,我们最常用的数据源方法是:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView;  

以上3个方法的作用就不过多赘述了.我们做iOS开发的天天都在和这3个方法打交道.所以我们的CCTableViewDataSource类中也要实现这3个方法
首先,创建一个CCTableViewDataSource类,类里面包含一个CCTableDataItem属性

@interface CCTableViewDataSource()
@property (nonatomic, strong) CCTableDataItem *dataItem;
@end

然后创建初始化方法

CCTableViewDataSource.h
- (instancetype)initWithDataItem:(CCTableDataItem *)dataItem;

CCTableViewDataSource.m
- (instancetype)initWithDataItem:(CCTableDataItem *)dataItem
{
    self = [super init];
    if (self) {
        self.dataItem = dataItem;
    }
    return self;
}

接着开始实现UITableView的数据源方法吧

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return self.dataItem.items.count;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    CCTableSectionDataItem *sectionItem = self.dataItem.items[section];
    
    return sectionItem.cells.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    CCTableSectionDataItem *sectionItem = self.dataItem.items[indexPath.section];
    CCTableCellDataItem *cellItem = sectionItem.cells[indexPath.row];
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellItem.cellClassName forIndexPath:indexPath];
    
    if ([cell isKindOfClass:[CCBaseTableViewCell class]]) {
        CCBaseTableViewCell *tableViewCell = (CCBaseTableViewCell *)cell;
        
        tableViewCell.delegate = cellItem.cellDelegate;
        
        [tableViewCell bindData:cellItem.cellData];
    }
    
    return cell;
}

很简单吧,现在我们开始实现Delegate

UITableViewDelegate的通用实现--CCTableViewDelegate

Delegate的实现就更加简单了.Delegate的实现更业务逻辑联系是非常紧密的,所以我们应该使用可定制的设计,这个时候就轮到block出场了.
针对Delegate的常用方法,我们都申明了一个block

//view
typedef UIView*(^ViewForHeaderInSection)(UITableView *tableView,NSInteger section,CCTableSectionDataItem *item);
typedef UIView*(^ViewForFooterInSection)(UITableView *tableView,NSInteger section,CCTableSectionDataItem *item);

//highlight
typedef void(^ShouldHighlightRowAtIndexPath)(UITableView *tableView,NSIndexPath *indexPath,id rowData);
typedef void(^DidHighlightRowAtIndexPath)(UITableView *tableView,NSIndexPath *indexPath,id rowData);
typedef void(^DidUnhighlightRowAtIndexPath)(UITableView *tableView,NSIndexPath *indexPath,id rowData);

//select
typedef NSIndexPath *(^WillSelectRowAtIndexPath)(UITableView *tableView,NSIndexPath *indexPath,id rowData, NSString *cellClassName);
typedef NSIndexPath *(^WillDeselectRowAtIndexPath)(UITableView *tableView,NSIndexPath *indexPath,id rowData, NSString *cellClassName);
typedef void(^DidSelectRowAtIndexPath)(UITableView *tableView,NSIndexPath *indexPath,id rowData, NSString *cellClassName);
typedef void(^DidDeselectRowAtIndexPath)(UITableView *tableView,NSIndexPath *indexPath,id rowData, NSString *cellClassName);

和DataSource一样,Delegate也会保有一个CCTableViewItem类型的属性

@interface CCTableViewDelegate()

@property (nonatomic, strong) CCTableDataItem *dataItem;

@end

然后创建初始化方法

CCTableViewDelegate.h
- (instancetype)initWithDataItem:(CCTableDataItem *)dataItem;

CCTableViewDelegate.m
- (instancetype)initWithDataItem:(CCTableDataItem *)dataItem
{
    self = [super init];
    if (self) {
        self.dataItem = dataItem;
    }
    return self;
}

接下来就是实现了UITableView的代理方法了

//display
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if ([cell isKindOfClass:[CCBaseTableViewCell class]]) {
        CCTableSectionDataItem *sectionItem = self.dataItem.items[indexPath.section];
        CCTableCellDataItem *cellItem = sectionItem.cells[indexPath.row];
        CCBaseTableViewCell *tableViewCell = (CCBaseTableViewCell *)cell;
        [tableViewCell cellWillDisplayByData:cellItem.cellData];
    }
}

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
    if ([view isKindOfClass:[CCBaseTableHeaderView class]]) {
        CCTableSectionDataItem *sectionItem = self.dataItem.items[section];
        CCBaseTableHeaderView *tableHeaderView = (CCBaseTableHeaderView *)view;
        
        [tableHeaderView headerWillDisplayByData:sectionItem.sectionHeaderData];
    }
}

- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section
{
    if ([view isKindOfClass:[CCBaseTableFooterView class]]) {
        CCTableSectionDataItem *sectionItem = self.dataItem.items[section];
        CCBaseTableFooterView *tableFooterView = (CCBaseTableFooterView *)view;
        
        [tableFooterView footerWillDisplayByData:sectionItem.sectionFooterData];
    }
}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath
{
    if ([cell isKindOfClass:[CCBaseTableViewCell class]]) {
        CCTableSectionDataItem *sectionItem = self.dataItem.items[indexPath.section];
        CCTableCellDataItem *cellItem = sectionItem.cells[indexPath.row];
        CCBaseTableViewCell *tableViewCell = (CCBaseTableViewCell *)cell;
        [tableViewCell cellDidEndDisplayByData:cellItem.cellData];
    }
}

- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section
{
    if ([view isKindOfClass:[CCBaseTableHeaderView class]]) {
        CCTableSectionDataItem *sectionItem = self.dataItem.items[section];
        CCBaseTableHeaderView *tableHeaderView = (CCBaseTableHeaderView *)view;
        
        [tableHeaderView headerDidEndDisplayByData:sectionItem.sectionHeaderData];
    }
}

- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section
{
    if ([view isKindOfClass:[CCBaseTableFooterView class]]) {
        CCTableSectionDataItem *sectionItem = self.dataItem.items[section];
        CCBaseTableFooterView *tableFooterView = (CCBaseTableFooterView *)view;
        
        [tableFooterView footerDidEndDisplayByData:sectionItem.sectionFooterData];
    }
}

//height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [self.dataItem cellHeightForIndexPath:indexPath];
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    CGFloat sectionHeaderHeight = [self.dataItem headerHeightForSection:section];

    return sectionHeaderHeight;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
    CGFloat sectionFooterHeight = [self.dataItem footerHeightForSection:section];

    return sectionFooterHeight;
}

//view
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    CCTableSectionDataItem *sectionItem = self.dataItem.items[section];
 
    if (self.viewForHeaderInSection) {
        UIView *customerSectionHeaderView = self.viewForHeaderInSection(tableView,section,sectionItem);
        return customerSectionHeaderView;
    }
    
    CGFloat sectionHeaderHeight = [self.dataItem headerHeightForSection:section];
    UIView *header;
    
    if (sectionItem.headerNibClass) {
        header = [sectionItem.headerNibClass viewFromXib];
    }else if (sectionItem.headerClass)
    {
        header = [[sectionItem.headerClass alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, sectionHeaderHeight)];
    }else
    {
        header = [UIView new];
    }
    
    
    if ([header isKindOfClass:[CCBaseTableHeaderView class]]) {
        CCBaseTableHeaderView *tableHeaderView = (CCBaseTableHeaderView *)header;
        [tableHeaderView bindData:sectionItem.sectionHeaderData];
        if (sectionItem.sectionHeaderDelegate) {
            tableHeaderView.delegate = sectionItem.sectionHeaderDelegate;
        }
    }
    return header;
}

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section
{
    CCTableSectionDataItem *sectionItem = self.dataItem.items[section];
    
    if (self.viewForFooterInSection) {
        UIView *customerSectionFooterView = self.viewForFooterInSection(tableView,section,sectionItem);
        return customerSectionFooterView;
    }
    
    CGFloat sectionFooterHeight = [self.dataItem footerHeightForSection:section];
    
    UIView *footer;
    if (sectionItem.footerNibClass) {
        footer = [sectionItem.footerNibClass viewFromXib];
    }else if (sectionItem.footerClass)
    {
        footer = [[sectionItem.footerClass alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, sectionFooterHeight)];
    }else
    {
        footer = [UIView new];
    }
    
    
    if ([footer isKindOfClass:[CCBaseTableFooterView class]]) {
        CCBaseTableFooterView *tableFooterView = (CCBaseTableFooterView *)footer;
        [tableFooterView bindData:sectionItem.sectionFooterData];
        if (sectionItem.sectionFooterDelegate) {
            tableFooterView.delegate = sectionItem.sectionFooterDelegate;
        }
    }
    return footer;
}

//highlighting
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.shouldHighlightRowAtIndexPath) {
        id rowData = [self.dataItem cellDataForIndexPath:indexPath];
        self.shouldHighlightRowAtIndexPath(tableView,indexPath,rowData);
    }
    return YES;
}

- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.didHighlightRowAtIndexPath) {
        id rowData = [self.dataItem cellDataForIndexPath:indexPath];
        self.didHighlightRowAtIndexPath(tableView,indexPath,rowData);
    }
}

- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.didUnhighlightRowAtIndexPath) {
        id rowData = [self.dataItem cellDataForIndexPath:indexPath];
        self.didUnhighlightRowAtIndexPath(tableView,indexPath,rowData);
    }
}

//select
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.willSelectRowAtIndexPath) {
        NSString *className = [self.dataItem cellClassNameForIndexPath:indexPath];
        id rowData = [self.dataItem cellDataForIndexPath:indexPath];
        self.willSelectRowAtIndexPath(tableView,indexPath,rowData,className);
    }
    
    return indexPath;
}

- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.willDeselectRowAtIndexPath) {
        NSString *className = [self.dataItem cellClassNameForIndexPath:indexPath];
        id rowData = [self.dataItem cellDataForIndexPath:indexPath];
        self.willDeselectRowAtIndexPath(tableView,indexPath,rowData,className);
    }
    return indexPath;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.didSelectRowAtIndexPath) {
        NSString *className = [self.dataItem cellClassNameForIndexPath:indexPath];
        id rowData = [self.dataItem cellDataForIndexPath:indexPath];
        self.didSelectRowAtIndexPath(tableView,indexPath,rowData,className);
    }
}

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (self.didDeselectRowAtIndexPath) {
        NSString *className = [self.dataItem cellClassNameForIndexPath:indexPath];
        id rowData = [self.dataItem cellDataForIndexPath:indexPath];
        self.didDeselectRowAtIndexPath(tableView,indexPath,rowData,className);
    }
}

大功告成

现在我们的封装就已经完成了,现在让我们来试一试这种方案是否能优化我们的编码流程

首先我们创建要显示到表上的表头,表尾,和Cell

@interface ExampleFooterItem : NSObject

@property (nonatomic, strong) NSString *titleString;
@property (nonatomic, strong) NSString *buttonString;

@end

@protocol ExampleFooterViewDelegate 

- (void)footerView:(ExampleFooterView *)headerView leftButtonClick:(UIButton *)click;

@end

@interface ExampleFooterView : CCBaseTableFooterView

/**
 如果需要消除警告,可以在.m文件中加入
 @synthesize delegate = _delegate;
 并实现getter和setter方法,不实现也不会出现问题
 */

@property (nonatomic, weak) id delegate;


@end
@interface ExampleHeaderView()
@property (weak, nonatomic) IBOutlet UILabel *exampleTitleLabel;
@property (weak, nonatomic) IBOutlet UIButton *rightExampleButton;

@end

@implementation ExampleHeaderView

@synthesize delegate = _delegate;


+ (CGFloat)headerViewHeightForData:(id)data
{
    return 40;
}

- (void)bindData:(ExampleHeaderItem *)data
{
    self.exampleTitleLabel.text = data.headerTitle;
    [self.rightExampleButton setTitle:data.buttonTitle forState:UIControlStateNormal];
}

- (IBAction)exampleButtonClick:(id)sender {
    if (self.delegate && [self.delegate respondsToSelector:@selector(headerView:rightButtonClick:)])
        [self.delegate headerView:self rightButtonClick:sender];
}

- (id)delegate
{
    return _delegate;
}

- (void)setDelegate:(id)delegate
{
    _delegate = delegate;
}


@end
@protocol ExampleCellDelegate 

- (void)exampleCell:(ExampleCell *)cell buttonClick:(UIButton *)sender;

@end

@interface ExampleCellItem : NSObject

@property (nonatomic, strong) NSString *titleString;
@property (nonatomic, strong) NSString *buttonString;
@property (nonatomic, strong) NSString *imageUrl;

@end

@interface ExampleCell : CCBaseTableViewCell

@property (nonatomic, weak) id delegate;

@end

然后在试图控制器中申明并初始化
CCTableDataItem
CCTableViewDelegate
CCTableViewDataSource

@property (nonatomic, strong) CCTableDataItem *dataItem;
@property (nonatomic, strong) CCTableViewDelegate *ccDelegate;
@property (nonatomic, strong) CCTableViewDataSource *ccDataSource;

- (CCTableDataItem *)dataItem
{
    if (!_dataItem) {
        _dataItem = [CCTableDataItem dataItem];
    }
    return _dataItem;
}

- (CCTableViewDelegate *)ccDelegate
{
    if (!_ccDelegate) {
        _ccDelegate = [CCTableViewDelegate delegateWithDataItem:self.dataItem];
    }
    return _ccDelegate;
}

- (CCTableViewDataSource *)ccDataSource
{
    if (!_ccDataSource) {
        _ccDataSource = [CCTableViewDataSource dataSourceWithItem:self.dataItem];
    }
    return _ccDataSource;
}

然后给Delegate设置一下单元格点击时候的事件

[self.ccDelegate setDidSelectRowAtIndexPath:^(UITableView *tableView, NSIndexPath *indexPath, id rowData, NSString *cellClassName) {
        
        [tableView deselectRowAtIndexPath:indexPath animated:YES];
        
        if ([rowData isKindOfClass:[ExampleCellItem class]]) {
            ExampleCellItem *cellItem = (ExampleCellItem *)rowData;
            NSLog(@"该单元格对应的数据源标题为:%@\n对应的按钮标题为:%@",cellItem.titleString,cellItem.buttonString);
        }
    }];

然后将要显示的数据装入dataItem中,

- (void)bindData
{
    [self.dataItem clearData];
    
    //创建无代理的区头区尾
    [self.dataItem addHeaderNibClass:[ExampleHeaderView class]
                      headerDataItem:[self.dataManager exampleHeaderData]
                      footerNibClass:[ExampleFooterView class]
                      footerDataItem:[self.dataManager exampleFooterData]];
    
    [self.dataItem addCellClass:[ExampleCell class] dataItem:[self.dataManager exampleCellData]];
    
    //创建有代理的区头区尾
    [self.dataItem addHeaderNibClass:[ExampleHeaderView class]
                      headerDataItem:[self.dataManager exampleHeaderDataWithDelegate]
                      headerDelegate:self footerNibClass:[ExampleFooterView class]
                      footerDataItem:[self.dataManager exampleFooterDataWithDelegate]
                      footerDelegate:self];
    
    [self.dataItem addCellClass:[ExampleCell class] dataItems:[self.dataManager exampleCellDatasWithDelegate] delegate:self];
   
}

最后

这种代理数据源封装方案基本到这里就全部说完了,这里是源码
里面是完成的封装以及提供了一些小的但很有用的工具类,同时CCBaseTableViewCell类还提供了返回动态Cell高度的方法,具体的使用开始看Demo,有很详细的注释

你可能感兴趣的:(iOS开发技巧--如何快速完成TableView布局及代理设置)