MYTableViewManager、TFTableViewDataSource浅析;

https://github.com/melvin7/MYTableViewManager

https://github.com/TimeFaceCoder/TFTableViewDataSource

TFTableViewDataSource 包含以下几个基本类

  • TFTableViewDataSourceConfig : 用于配置列表数据获取接口,处理函数,分页记录数等
  • TFTableViewDataSource : TableView数据源处理类,通过继承TFTableViewDataSource 可以重载下拉刷新,列表展示等方法来实现自定义
  • TFTableViewDataManager 列表数据处理工具,所有的列表数据处理都需要继承TFTableViewDataManager来处理
  • TFTableViewItem 单行列表数据源与事件处理类,所有的列表数据源都需要继承TFTableViewItem来处理
  • TFTableViewItemCell 单行列表UI,所有的列表UI都需要继承TFTableViewItemCell来处理

1、回头看看,这框架还是很模糊啊;

DCSwitchTableViewItem

处理item点击事件有,onViewClickHandler,selectionHandler等;

2、DCManegerDataManeger,

TFTableViewDataManager初始化时initWithDataSource,会设置下_cellViewClickHandler;会先处理代理VC的事件

if ([strongSelf.tableViewDataSource.delegate respondsToSelector:@selector(actionOnView:actionType:)]) 

3、tableView Cell 响应事件;

DCManagerTableViewItem setSelectionHandler:)

tableView:didSelectRowAtIndexPath:

调用 MYTableViewItem 的 selectionHandler

TFTableViewItem : MYTableViewItem

单行列表数据源和事件处理类; 所有的列表数据源都要继承TFTableViewItem来处理;

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }
    __weak typeof(self) weakself = self;
    self.selectionHandler = ^(id item){
        if (weakself.onViewClickHandler) {
            weakself.onViewClickHandler(item, -1);
        }
    };
    return self;
}

在TFTableViewItem的init方法中初始化了父类MYTableViewItem的selectionHandler;
里面调用自己的属性onViewClickHandler;而其初始化在temWithModel:clickHandler:中;

@property (nonatomic ,copy) void (^onViewClickHandler)(TFTableViewItem *item,NSInteger actionType);

???selectionHandler、onViewClickHandler在哪里调用的;

TFTableViewItemCell : MYTableViewCell

单行列表UI,所有的列表UI都继承自TFTableViewItemCell来处理;

没有code, 主要看其父类MYTableViewCell : ASCellNode

- (instancetype)initWithTableViewItem:(MYTableViewItem *)tableViewItem {
    self = [super init];
    if(self) {
        self.tableViewItem = tableViewItem;
        
        // hairline cell separator
        if (self.tableViewItem.separatorStyle != UITableViewCellSeparatorStyleNone) {
            _dividerNode = [[ASDisplayNode alloc] init];
            _dividerNode.backgroundColor = self.tableViewItem.dividerColor;
            [self addSubnode:_dividerNode];
        }
        [self initCell];
    }
    return self;
}

MYTableViewCell继承自ASCellNode;整体基于ASTableView实现;
在initWithTableViewItem方法中设置列表数据源self.tableViewItem;调用initCell方法;

所以在我们自定义的cell中,在initCell中初始化UI;

ASDisplayKit中layout相当于layoutSubviews;重新布局;分为自动布局、手动布局;

TFTableViewDataManager : NSObject<TFTableViewDataManagerProtocol>

列表数据处理工具,所有的列表数据处理都需要继承TFTableViewDataManager来处理;

管理类,管理cell数据源item,cell的UI-cell;

@interface TFTableViewDataManager : NSObject<TFTableViewDataManagerProtocol>

@property (nonatomic ,weak) TFTableViewDataSource *tableViewDataSource;

/**
 *  列表内点击事件 block
 */
@property (nonatomic ,copy) CellViewClickHandler   cellViewClickHandler;

/**
 *  列表删除事件 block
 */
@property (nonatomic ,copy) DeletionHandlerWithCompletion deleteHanlder;

/**
 *  当前cell的索引indexPath
 */
@property (nonatomic ,strong) NSIndexPath *currentIndexPath;

/**
 *  对应的listType,绑定url
 */
@property (nonatomic ,assign) NSInteger listType;

/**
 *  清除上面的block
 */
- (void)clearCompletionBlock;

@end

查看TFTableViewDataManager的实现;发现其initWithDataSource在dataSource中调用;
manager对dataSource弱引用;dataSource中对manager强引用;避免循环引用;

@implementation TFTableViewDataManager

- (instancetype)initWithDataSource:(TFTableViewDataSource *)tableViewDataSource
                          listType:(NSInteger)listType {
    self = [super init];
    if (!self) {
        return nil;
    }
    _tableViewDataSource = tableViewDataSource;
    _listType = listType;
    __weak __typeof(self)weakSelf = self;
    _cellViewClickHandler = ^ (TFTableViewItem *item ,NSInteger actionType) {
        __typeof(&*weakSelf) strongSelf = weakSelf;
        strongSelf.currentIndexPath = item.indexPath;
        [item deselectRowAnimated:YES];
        if ([strongSelf.tableViewDataSource.delegate respondsToSelector:@selector(actionOnView:actionType:)]) {
            [strongSelf.tableViewDataSource.delegate actionOnView:item actionType:actionType];
        }
        [strongSelf cellViewClickHandler:item actionType:actionType];
    };
    
    _deleteHanlder = ^(TFTableViewItem *item ,Completion completion) {
        __typeof(&*weakSelf) strongSelf = weakSelf;
        [strongSelf deleteHanlder:item completion:completion];
    };
    return self;
}

/**
 *  显示列表数据
 *
 *  @param result          数据字典
 *  @param completionBlock 回调block
 */
- (void)reloadView:(NSDictionary *)result block:(TableViewReloadCompletionBlock)completionBlock {
    
}
/**
 *  列表内View事件处理
 *
 *  @param item
 *  @param actionType
 */
- (void)cellViewClickHandler:(TFTableViewItem *)item actionType:(NSInteger)actionType {
    self.currentIndexPath = item.indexPath;
}
/**
 *  列表删除事件处理
 *
 *  @param item
 */
- (void)deleteHanlder:(TFTableViewItem *)item completion:(void (^)(void))completion {
    self.currentIndexPath = item.indexPath;
}

/**
 *  刷新指定Cell
 *
 *  @param actionType
 *  @param dataId
 */
- (void)refreshCell:(NSInteger)actionType identifier:(NSString *)identifier {
    
}


- (void)clearCompletionBlock {
    self.cellViewClickHandler = nil;
    self.deleteHanlder        = nil;
}

@end

在)initWithDataSource:listType:中初始化了cellViewClickHandler、deleteHanlder;
其中cellViewClickHandler中会回调datasource的代理类(通常为TFTableViewController的子类);这样就将cell的事件回传到ViewController了;

_cellViewClickHandler = ^ (TFTableViewItem *item ,NSInteger actionType) {
    __typeof(&*weakSelf) strongSelf = weakSelf;
    strongSelf.currentIndexPath = item.indexPath;
    [item deselectRowAnimated:YES];
    if ([strongSelf.tableViewDataSource.delegate respondsToSelector:@selector(actionOnView:actionType:)]) {
        [strongSelf.tableViewDataSource.delegate actionOnView:item actionType:actionType];
    }
    [strongSelf cellViewClickHandler:item actionType:actionType];
};

看看协议接口TFTableViewDataManagerProtocol有哪些东西;

ifndef TFTableViewDataManagerProtocol_h
#define TFTableViewDataManagerProtocol_h

#import "TFTableViewDataSource.h"
#import <TFNetwork/TFNetwork.h>

@class TFTableViewItem;
@class MYTableViewSection;
typedef void (^Completion)(void);
typedef void (^CellViewClickHandler)(__kindof TFTableViewItem *item ,NSInteger actionType);
typedef void (^DeletionHandlerWithCompletion)(__kindof TFTableViewItem *item, void (^)(void));
typedef void (^TableViewReloadCompletionBlock)(BOOL finished,id object,NSError *error, NSArray <MYTableViewSection *> *sections);


@protocol TFTableViewDataManagerProtocol <NSObject>

@required
/**
 *  列表业务类初始化
 *
 *  @param tableViewDataSource 列表数据源
 *  @param listType            列表类型
 *
 *  @return TFTableDataSourceManager
 */
- (instancetype)initWithDataSource:(TFTableViewDataSource *)tableViewDataSource
                          listType:(NSInteger)listType;

/**
 *  显示列表数据
 *
 *  @param result          数据字典
 *  @param completionBlock 回调block
 */
- (void)reloadView:(NSDictionary *)result block:(TableViewReloadCompletionBlock)completionBlock;
/**
 *  列表内View事件处理
 *
 *  @param item
 *  @param actionType
 */
- (void)cellViewClickHandler:(TFTableViewItem *)item actionType:(NSInteger)actionType;
/**
 *  列表删除事件处理
 *
 *  @param item
 */
- (void)deleteHanlder:(TFTableViewItem *)item completion:(void (^)(void))completion;

/**
 *  刷新指定Cell
 *
 *  @param actionType
 *  @param dataId
 */
- (void)refreshCell:(NSInteger)actionType identifier:(NSString *)identifier;

@end

#endif

TFTableViewDataSource : NSObject

TabelView数据源处理类,通过继承TFTableViewDataSource可以重载下拉刷新、列表展示等方法来实现自定义;

具体使用可以查看TFTableViewController;总体的逻辑参考RETableViewManager;两步:

  1. 绑定tableView到MYTableViewManager;
  2. 给tableViewManager手动设置item和itemCell;
_tableViewManager = [[MYTableViewManager alloc] initWithTableView:self.tableView delegate:nil];
_tableViewManager[@"TFTableviewDefaultItem"] = @"TFTableviewDefaultItemCell";

查看头文件

@protocol TFTableViewDataSourceDelegate <NSObject>

@required
/**
 *  列表及其控件点击事件回调
 *
 *  @param item
 *  @param actionType 事件类型
 */
- (void)actionOnView:(TFTableViewItem *)item actionType:(NSInteger)actionType;
/**
 *  开始加载
 */
- (void)didStartLoad;
/**
 *  加载完成
 *
 *  @param loadPolicy 加载类型
 *  @param object     返回数据
 *  @param error      错误
 */
- (void)didFinishLoad:(TFDataLoadPolicy)loadPolicy object:(id)object error:(NSError *)error;

@optional
- (BOOL)showPullRefresh;

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;

- (void)scrollViewDidScrollUp:(CGFloat)deltaY;

- (void)scrollViewDidScrollDown:(CGFloat)deltaY;

- (void)scrollFullScreenScrollViewDidEndDraggingScrollUp;

- (void)scrollFullScreenScrollViewDidEndDraggingScrollDown;

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath;

- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath;

- (void)tableView:(ASTableView *)tableView didEndDisplayingNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath;

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath;

@end

@interface TFTableViewDataSource : NSObject

@property (nonatomic ,weak) id<TFTableViewDataSourceDelegate> delegate;
@property (nonatomic ,strong ,readonly ,getter = manager) MYTableViewManager *manager;
@property (nonatomic ,weak) ASTableView *tableView;
@property (nonatomic ,assign) TFDataSourceState dataSourceState;

/**
 *  总页数
 */
@property (nonatomic ,assign) NSInteger totalPage;

/**
 *  当前页码
 */
@property (nonatomic ,assign) NSInteger currentPage;

/**
 *  对应的listType,绑定url
 */
@property (nonatomic ,assign) NSInteger listType;

/**
 *  列表数据缓存时间
 */
@property (nonatomic ,assign) NSInteger cacheTimeInSeconds;

/**
 *  初始化化方法,用于绑定manager和tableview;item和itemCell;
 *
 */
- (instancetype)initWithTableView:(ASTableView *)tableView
                         listType:(NSInteger)listType
                           params:(NSDictionary *)params
                         delegate:(id /*<TFTableViewDataSourceDelegate>*/)delegate;

/**
 *  开始加载,没有url参数
 */
- (void)startLoading;

/**
 *  开始加载列表数据,带url参数
 *
 *  @param params GET 请求参数
 */
- (void)startLoadingWithParams:(NSDictionary *)params;

/**
 *  停止加载
 */
- (void)stopLoading;

/**
 *  刷新指定Cell
 *
 *  @param actionType 刷新动作
 *  @param identifier Cell唯一标示
 */
- (void)refreshCell:(NSInteger)actionType identifier:(NSString *)identifier;


/**
 *  下拉刷新相关
 */
- (void)initTableViewPullRefresh;
- (void)startTableViewPullRefresh;
- (void)stopTableViewPullRefresh;

@end

查看实现方法:

一. 初始化init开始;

- (instancetype)initWithTableView:(ASTableView *)tableView
                         listType:(NSInteger)listType
                           params:(NSDictionary *)params
                         delegate:(id /*<TFTableViewDataSourceDelegate>*/)delegate {
    self = [super init];
    if (!self) {
        return nil;
    }
    _delegate  = delegate;
    _tableView = tableView;
    _listType  = listType;
    _requestArgument = [NSMutableDictionary dictionaryWithDictionary:params];
    _manager = [[MYTableViewManager alloc] initWithTableView:tableView delegate:self];
    [self initTableViewPullRefresh];
    [self setupDataSource];
    return self;
}

初始化请求参数requestArgument,MYTableViewManager;调用initTableViewPullRefresh初始化下拉刷新;调用setupDataSource方法,进行一些初始化配置,根据listType获取对应的url和dataManager;

#pragma mark - 初始化数据加载方法
- (void)setupDataSource {
    _downThresholdY = 200.0;
    _upThresholdY = 25.0;
    
    NSString *requestURL = [[TFTableViewDataSourceConfig sharedInstance] requestURLByListType:_listType];
    NSString *className = [[TFTableViewDataSourceConfig sharedInstance] classNameByListType:_listType];
    _dataRequest = [[TFTableViewDataRequest alloc] initWithRequestURL:requestURL params:_requestArgument];
    if (className) {
        Class class = NSClassFromString(className);
        _tableViewDataManager = [[class alloc] initWithDataSource:self listType:_listType];
    }
    //registerClass
    NSArray *itemClassList = [TFTableViewClassList subclassesOfClass:[MYTableViewItem class]];
    for (Class itemClass in itemClassList) {
        NSString *itemName = NSStringFromClass(itemClass);
        self.manager[itemName] = [itemName stringByAppendingString:@"Cell"];
    }
}

在RouteManager中setupMap配置信息如下;

[self mapWithListType:ListTypeManagerCreateList
     dataManagerClass:@"DCManegerDataManeger"
                  url:kDCInterfaceManagerRoot];

看看mapWithListType怎么实现的;最后调用TFTableViewDataSourceConfig方法;

+ (void)mapWithListType:(ListType)listType dataManagerClass:(NSString *)className url:(NSString *)url {
    [[TFTableViewDataSourceConfig sharedInstance]
     mapWithListType:listType
     mappingInfo:@{ kTFTableViewDataManagerClassKey : className,
                    kTFTableViewDataRequestURLKey   : url }];
}

返回查看setupDataSource方法;就获取到相应的requestURL和ViewDataManager类名;
对DataManager进行实例化;通过TFTableViewClassList获取所有MYTableViewItem的子类,这是通过runtime实现的,之后给manger配置item和itemCell,像下面这样;

_tableViewManager = [[MYTableViewManager alloc] initWithTableView:self.tableView delegate:nil];
_tableViewManager[@"TFTableviewDefaultItem"] = @"TFTableviewDefaultItemCell";

二. 数据加载loading;

// 开始加载,
- (void)startLoading {
    [self startLoadingWithParams:_requestArgument];
}

// 带参数 加载;
- (void)startLoadingWithParams:(NSDictionary *)params {
    if (_requestArgument) {
        [_requestArgument addEntriesFromDictionary:params];
    }
    else {
        _requestArgument = [NSMutableDictionary dictionaryWithDictionary:params];
    }
    [self load:TFDataLoadPolicyNone context:nil];
}

???疑问,这样调用,参数会重复吗;

[self.requestParams setObject:@(model.id) forKey:@"city"];
[self.dataSource startLoadingWithParams:self.requestParams];

数据加载核心方法

// 数据加载
- (void)load:(TFDataLoadPolicy)loadPolicy context:(ASBatchContext *)context {
    //当前正在加载数据
    if (_dataSourceState == TFDataSourceStateLoading) {
        return;
    }
    if (loadPolicy == TFDataLoadPolicyMore) {
        //加载下一页数据
        if (_currentPage == _totalPage) {
            //加载完所有页码
            _dataSourceState = TFDataSourceStateFinished;
            return;
        }
        _currentPage++;
    } else {
        _currentPage = 1;
        _totalPage = 1;
    }
    [_requestArgument setObject:[NSNumber numberWithInteger:[TFTableViewDataSourceConfig pageSize]]
                         forKey:@"pageSize"];
    [_requestArgument setObject:[NSNumber numberWithInteger:_currentPage] forKey:@"currentPage"];
    _dataRequest.requestArgument    = _requestArgument;
    // 设置缓存时间
    _dataRequest.cacheTimeInSeconds = _cacheTimeInSeconds;
    // 设置操作标示
    _dataSourceState = TFDataSourceStateLoading;
    // 加载第一页时候使用缓存数据
    if ([_dataRequest cacheResponseObject] && !_firstLoadOver) {
        // 使用缓存数据绘制UI
        TFTableViewLogDebug(@"use cache data for %@",_dataRequest.requestURL);
        [self handleResultData:[_dataRequest cacheResponseObject]
                dataLoadPolicy:TFDataLoadPolicyCache
                       context:context
                         error:nil];
    }
    else {
        // 请求网络数据
        [_dataRequest startWithCompletionBlockWithSuccess:^(__kindof TFBaseRequest *request) {
            TFTableViewLogDebug(@"get data from server %@ page:%@",request.requestUrl,@(_currentPage));
            [self handleResultData:request.responseObject dataLoadPolicy:loadPolicy context:context error:nil];
        } failure:^(__kindof TFBaseRequest *request) {
            TFTableViewLogDebug(@"get data from %@ error :%@ userinfo:%@",request.requestUrl,request.error,request.userInfo);
            // 网络请求出错,存在缓存,先获取缓存
            if ([request cacheResponseObject]) {
                [self handleResultData:[request cacheResponseObject]
                        dataLoadPolicy:loadPolicy
                               context:context
                                 error:nil];
            }
            else {
                [self handleResultData:nil
                        dataLoadPolicy:loadPolicy
                               context:context
                                 error:request.error];
            }
        }];
    }
}

设置请求参数、请求缓存时间;有缓存先用缓存绘制UI,不然网络请求数据;网络请求出现错误的时候,也会先使用缓存;

// 处理返回数据并绘制UI
- (void)handleResultData:(NSDictionary *)result
          dataLoadPolicy:(TFDataLoadPolicy)dataLoadPolicy
                 context:(ASBatchContext *)context
                   error:(NSError *)error {
    TFTableViewLogDebug(@"%s",__func__);
    NSError *hanldeError = nil;
    NSInteger lastSectionIndex = [[self.manager sections] count] - 1;
    if (!result || [[result objectForKey:@"dataList"] count] <= 0) {
        //数据为空
        hanldeError = [NSError errorWithDomain:@"" code:1 userInfo:@{}];
    }
    if (dataLoadPolicy == TFDataLoadPolicyMore) {
        //加载下一页,移除loading item
        [self.manager removeLastSection];
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:lastSectionIndex]
                          withRowAnimation:UITableViewRowAnimationFade];
        });
    }
    [self setTotalPage:[[result objectForKey:@"totalPage"] integerValue]];
    if (_totalPage == 0) {
        //数据边界检查
        _totalPage = 1;
        _currentPage = 1;
    }
    
    // 调用tableViewManager 的 reloadView:block方法 显示列表数据;
    // 在这里将数据传给DataManager的子类;调用block
    __weak __typeof(self)weakSelf = self;
    [self.tableViewDataManager reloadView:result
                                    block:^(BOOL finished, id object, NSError *error, NSArray <MYTableViewSection *> *sections)
     {
         typeof(self) strongSelf = weakSelf;
         if (finished) {
             if (dataLoadPolicy == TFDataLoadPolicyReload || dataLoadPolicy == TFDataLoadPolicyNone) {
                 // 重新加载列表数据
                 [strongSelf.manager removeAllSections];
             }
             NSInteger rangelocation = [strongSelf.manager.sections count];
             [strongSelf.manager addSectionsFromArray:sections];
             NSInteger rangelength = 1;
             // 需要在主线程执行
             if (_currentPage < _totalPage) {
                 // 存在下一页数据,在列表尾部追加loading item
                 MYTableViewSection *section = [MYTableViewSection section];
                 // loading item
                 [section addItem:[MYTableViewLoadingItem itemWithTitle:NSLocalizedString(@"正在加载...", nil)]];
                 [strongSelf.manager addSection:section];
                 rangelength += sections.count;
             }
             dispatch_async(dispatch_get_main_queue(), ^{
                 if (dataLoadPolicy == TFDataLoadPolicyMore) {
                     [strongSelf.tableView insertSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(rangelocation, rangelength)]
                                         withRowAnimation:UITableViewRowAnimationFade];
                     if (context) {
                         [context completeBatchFetching:YES];
                     }
                 }
                 else {
                     [strongSelf reloadTableView];
                     strongSelf.firstLoadOver = YES;
                     if (dataLoadPolicy == TFDataLoadPolicyReload) {
                         [strongSelf stopTableViewPullRefresh];
                     }
                     if (dataLoadPolicy == TFDataLoadPolicyCache) {
                         //第一次从缓存加载数据后延迟触发下拉刷新重新加载
                         [strongSelf performSelector:@selector(startTableViewPullRefresh)
                                          withObject:nil
                                          afterDelay:0.75];
                     }
                 }
                 // 数据加载完成
                 if (strongSelf.delegate && [strongSelf.delegate respondsToSelector:@selector(didFinishLoad:object:error:)]) {
                     [strongSelf.delegate didFinishLoad:dataLoadPolicy object:object error:error?error:hanldeError];
                 }
             });
             strongSelf.dataSourceState = TFDataSourceStateFinished;
         }
     }];
}


调用tableViewManager 的 reloadView:block方法 显示列表数据;在这里将数据传给DataManager的子类;调用block;

// 重新加载列表数据;remove所有section了;
[strongSelf.manager removeAllSections];

最后数据加载完成,回调dataSource的代理(通常为TFTableView的子类);
didFinishLoad:object:error:

通过源码看到,reloadView中将数据通过block传过去,在VC中didFinishLoad才能进行判断;
DataManager 给 ViewController 传数据;
??? 那反过来呢;

typedef void (^TableViewReloadCompletionBlock)(BOOL finished,id object,NSError *error, NSArray <MYTableViewSection *> *sections);

- (void)reloadView:(NSDictionary *)result block:(TableViewReloadCompletionBlock)completionBlock

completionBlock(YES, list, nil, @[section]);

- (void)didFinishLoad:(TFDataLoadPolicy)loadPolicy object:(id)object error:(NSError *)error
{
    [super didFinishLoad:loadPolicy object:object error:error];
    
    if (!object)
    {
        [self showStateView:kTFViewStateNoData];
    }
}

MYTableViewManager

最终tableView的cell绘制都在这个MYTableViewManager中;
MYTableViewManager的整体逻辑可以参考RETableViewManager;只是将RE里的UITableView
换成ASTableView;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

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

// 返回对应的cell;ASTableView中称为node;
- (ASCellNode *)tableView:(ASTableView *)tableView nodeForRowAtIndexPath:(NSIndexPath *)indexPath

// tableView的侧边IndexTitles
- (NSArray *)sectionIndexTitlesForTableView:(ASTableView *)tableView

// tableView尾视图Header 的 title
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section

// tableView尾视图Footer 的 title
- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section

// tableView移动move
// 可以看到moveHandler、moveCompletionHandler功能;怎么用的
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath

- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath

// 看到给item添加editingStyle,就能实现左划删除等;
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath


- (void)tableView:(ASTableView *)tableView willDisplayNodeForRowAtIndexPath:(NSIndexPath *)indexPath

- (void)tableView:(ASTableView *)tableView didEndDisplayingNode:(ASCellNode *)node forRowAtIndexPath:(NSIndexPath *)indexPath


tableView如何实现删除,

// tableView如何实现删除; section删掉,tableView删掉,还要修改后面
[section removeItemAtIndex:indexPath.row];
[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];

for (NSInteger i = indexPath.row; i < section.items.count; i++) {
    MYTableViewItem *afterItem = [[section items] objectAtIndex:i];
    MYTableViewCell *cell = (MYTableViewCell *)[(ASTableView *)tableView nodeForRowAtIndexPath:afterItem.indexPath];
    cell.rowIndex--;
}

Tableview delegate

// Header、Footer
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section

- (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section

- (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section

- (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section

// header、Footer的高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)sectionIndex

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)sectionIndex

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectionIndex

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)sectionIndex

// Accessories (disclosures).
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath

// Selection
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath

- (void)tableView:(UITableView *)tableView didHighlightRowAtIndexPath:(NSIndexPath *)indexPath

- (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath

tableView 事件处理

- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath

- (NSIndexPath *)tableView:(UITableView *)tableView willDeselectRowAtIndexPath:(NSIndexPath *)indexPath

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath

// Editing
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath

// 左滑删除、标题;
- (NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath

- (BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath

- (void)tableView:(UITableView*)tableView willBeginEditingRowAtIndexPath:(NSIndexPath *)indexPath

- (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath

// Moving/reordering

- (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath

// Indentation

- (NSInteger)tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath

// Copy/Paste.  All three methods must be implemented by the delegate.
// 复制/粘贴;
- (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath

- (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender

其他的代理不看了;分析下tableView的事件处理;

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    [_tableView deselectRowAtIndexPath:indexPath animated:YES];
    [_tableView beginUpdates];
    MYTableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section];
    id item = [section.items objectAtIndex:indexPath.row];
    if ([item respondsToSelector:@selector(setSelectionHandler:)]) {
        MYTableViewItem *actionItem = (MYTableViewItem *)item;
        if (actionItem.selectionHandler)
            actionItem.selectionHandler(item);
    }
    
    // Forward to UITableView delegate
    //
    if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)])
        [self.delegate tableView:tableView didSelectRowAtIndexPath:indexPath];
    
    [_tableView endUpdates];
}

获取相应的section,获取item;调用item的selectionHandler,并将item作为block参数回传过去;

TFTableViewItem 在初始化会设置selectionHandler

- (instancetype)init {
    self = [super init];
    if (!self) {
        return nil;
    }
    __weak typeof(self) weakself = self;
    self.selectionHandler = ^(id item){
        if (weakself.onViewClickHandler) {
            weakself.onViewClickHandler(item, -1);
        }
    };
    return self;
}

+ (instancetype)itemWithModel:(NSObject *)model
                     clickHandler:(void(^)(TFTableViewItem *item,NSInteger actionType))clickHandler {
    
    TFTableViewItem *item = [[[self class] alloc] init];
    item.model = model;
    item.onViewClickHandler = clickHandler;
    return item;
}

在init中将actionType设为-1;作为整个cell的点击;其他非零值可以为cell上的按钮;并且将item值回传给onViewClickHandler;

TFTableViewDataManager中会初始化cellViewClickHandler;其中的item是TFTableViewItem传过来的;

- (instancetype)initWithDataSource:(TFTableViewDataSource *)tableViewDataSource
                          listType:(NSInteger)listType {
    self = [super init];
    if (!self) {
        return nil;
    }
    _tableViewDataSource = tableViewDataSource;
    _listType = listType;
    __weak __typeof(self)weakSelf = self;
    _cellViewClickHandler = ^ (TFTableViewItem *item ,NSInteger actionType) {
        __typeof(&*weakSelf) strongSelf = weakSelf;
        strongSelf.currentIndexPath = item.indexPath;
        [item deselectRowAnimated:YES];
        if ([strongSelf.tableViewDataSource.delegate respondsToSelector:@selector(actionOnView:actionType:)]) {
            [strongSelf.tableViewDataSource.delegate actionOnView:item actionType:actionType];
        }
        [strongSelf cellViewClickHandler:item actionType:actionType];
    };
    
    _deleteHanlder = ^(TFTableViewItem *item ,Completion completion) {
        __typeof(&*weakSelf) strongSelf = weakSelf;
        [strongSelf deleteHanlder:item completion:completion];
    };
    return self;
}

cellViewClickHandler中,
首先会调用tableViewDataSource.delegate的方法actionOnView:actionType:; 一般为TFTableViewController的子类;
然后再调用对应TFTableViewDataManager子类的cellViewClickHandler:actionType:

Objective-C如何自己实现一个基于数组下标的属性访问模式

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容

  • 许多 iOS 应用程序向用户显示列表项,并允许用户选择,删除或重新排列列表项。 不管是显示用户地址簿中的人员列表的...
    titvax阅读 1,477评论 2 1
  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,155评论 1 23
  • 2017.02.22 可以练习,每当这个时候,脑袋就犯困,我这脑袋真是神奇呀,一说让你做事情,你就犯困,你可不要太...
    Carden阅读 1,316评论 0 1
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,975评论 4 60
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,259评论 25 707