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閱讀 229,362評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,013評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,346評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,421評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,146評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,534評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,585評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,767評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,318評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,074評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,258評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,828評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,486評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,916評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,156評論 1 290
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,993評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,234評論 2 375

推薦閱讀更多精彩內容

  • 許多 iOS 應用程序向用戶顯示列表項,并允許用戶選擇,刪除或重新排列列表項。 不管是顯示用戶地址簿中的人員列表的...
    titvax閱讀 1,536評論 2 1
  • iOS網絡架構討論梳理整理中。。。 其實如果沒有APIManager這一層是沒法使用delegate的,畢竟多個單...
    yhtang閱讀 5,231評論 1 23
  • 2017.02.22 可以練習,每當這個時候,腦袋就犯困,我這腦袋真是神奇呀,一說讓你做事情,你就犯困,你可不要太...
    Carden閱讀 1,364評論 0 1
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,156評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,692評論 25 708