玩轉UITableView系列(一)--- 解耦封裝、簡化代碼、適者生存!

来源:http://www.cnblogs.com/beckwang0912/archive/2017/06/29/7082826.html
-Advertisement-
Play Games

UITableView這個iOS開發中永遠繞不開的UIView,那麼就不可避免的要在多個頁面多種場景下反覆摩擦UITableView,就算是剛跳進火坑不久的iOS Developer也知道實現UITableView的數據源dataSource和代理delegate,寫出一個UITableView也就 ...


UITableView這個iOS開發中永遠繞不開的UIView,那麼就不可避免的要在多個頁面多種場景下反覆摩擦UITableView,就算是剛跳進火坑不久的iOS Developer也知道實現UITableView的數據源dataSource和代理delegate,寫出一個UITableView也就基本OK了,但是這僅僅是寫出一個UITableView而已,作為一個有想法的程式猿,要做的還有很多,如何利用UITableViewCell的重用機制,如何提高性能等,這些留在後面的系列中一一講述,那麼本文要解決的痛點又是什麼呢?回答這個問題之前,我們先來看看上面提到的UITableView的兩大核心:UITableViewDataSource、UITableViewDelegate!

 

一、UITableViewDataSource

UITableView需要一個數據源(dataSource)來顯示數據,UITableView會向數據源查詢一共有多少行數據以及每一行顯示什麼數據等。沒有設置數據源的UITableView只是個空殼。凡是遵守UITableViewDataSource協議的OC對象,都可以是UITableView的數據源。查看源碼:

@required  // 必須實現

// 每個section的行數
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;

// 第section分區第row行的UITableViewCell對象 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

@optional  // 可選實現

// section個數,預設是1
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; 

// 第section分區的頭部標題
- (nullable NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section;    

// 第section分區的底部標題 
- (nullable NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section;

// 某一行是否可以編輯(刪除)  
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath;

// 某一行是否可以移動來進行重新排序 
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath;

// UITableView右邊的索引欄的內容  
// return list of section titles to display in section index view (e.g. "ABCD...Z#")
- (nullable NSArray<NSString *> *)sectionIndexTitlesForTableView:(UITableView *)tableView;                              
UITableViewDataSourc   二、UITableViewDelegate 通常都要為UITableView設置代理對象(delegate),以便在UITableView觸發一下事件時做出相應的處理,比如選中了某一行。凡是遵守了UITableViewDelegate協議的OC對象,都可以是UITableView的代理對象。一般會讓控制器充當UITableView的dataSource和delegate。查看源碼:
@protocol UITableViewDelegate<NSObject, UIScrollViewDelegate>

@optional

// 每行高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

// 每個section頭部高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section;

// 每個section底部高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section;

// 每個section頭部自定義UIView
- (nullable UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section; 

// 每個section底部自定義UIView
- (nullable UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section;

// 是否允許高亮
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);

// 選中某行
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
UITableViewDelegate

 

到這裡已經很明確了,在需要實現UITableView的控制器對象里,就不可避免的要設置數據源和設置代理,那麼就不可避免的需要實現以上提到的那些代理方法,試想一下,如果不進行有效的封裝,那極有可能每個需要UITableView的Controller里都有如下重覆的代碼行:

#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 0.000001;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
    return 0.000001;
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return 0;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 0.000001;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 0;
}

- (UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
    return nil;
}

- (UIView*)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
    return nil;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"defaultType"];
    return  cell;
}

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

// lazy load
- (UITableView*)tableView{
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, -64, KS_Width, KS_Heigth+64) style:UITableViewStyleGrouped];
        _tableView.delegate = (id)self;
        _tableView.dataSource = (id)self;
        [_tableView setSectionHeaderHeight:0];
        _tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
        _tableView.showsVerticalScrollIndicator = NO;
        _tableView.showsHorizontalScrollIndicator = NO;
    }
    return _tableView;
}
重覆代碼塊

這已經是夠災難的了,如果在項目周期中再遇到某個或者多個頁面設計UI設計頻繁的變動,那簡直不敢想象,哪怕每次只是一點小小的改動,也可能需要修改上面重覆代碼塊中UITableViewDelegate的多個地方,如新插入一行row或者一個section,所有涉及到section或者row的地方或許都需要更改!!!

OK,我現在可以回答上面的問題了,這邊文章到底是做什麼的?解決的痛點在那裡?--- 解耦封裝、簡化代碼、適者生存!

從重覆代碼塊我們可以看出,一般會讓控制器充當UITableView的dataSource和delegate,那麼既然要解耦,那麼就要打破思維定式,讓UITableView自己做自己的dataSource和delegate!畢竟我的地盤我做主嘛!其次將UITableViewCell進行block封裝對象化,讓其所有的屬性都自我集成

一、首先來看UITableViewCell的封裝 -- ZTCoolTableViewCell

@class UIView;
@class UITableViewCell;
@class UITableView;
@class NSIndexPath;

// 創建section頭部 Or section底部的block
typedef UIView *(^buildCell)(UITableView *tableView, NSInteger section);
// 創建section對應的row數據源的block
typedef UITableViewCell *(^buildCellInfo)(UITableView *tableView, NSIndexPath *indexPath);
// 點擊section對應row的事件block
typedef void (^clickBlock)(UITableView *tableView, NSIndexPath *indexPath);
// ZTCoolTableCellList刷新block
typedef void (^refreshBlock)();

@interface ZTCoolTableViewCell : NSObject

// 行高度
@property (nonatomic,assign) CGFloat height;
// 構造行
@property (nonatomic, copy) buildCell buildCell;

@end

@interface ZTCoolTableCellList : NSObject

// 頭部
@property (nonatomic,strong) ZTCoolTableViewCell * headCell;
// 底部
@property (nonatomic,strong) ZTCoolTableViewCell * footCell;
// 構造行
@property (nonatomic,copy) buildCellInfo buildCellInfo;
// 列高(等於0表示自適應)
@property (nonatomic,assign) CGFloat cellHeigth;
// 行數量
@property (nonatomic,assign) NSInteger cellCount;
// 行點擊事件
@property(nonatomic,copy) clickBlock clickBlock;
// 刷新事件(適用於需要動態更新tableview佈局:新增或者刪減section/row)
@property(nonatomic,copy) refreshBlock refreshBlock;
// 行標識
@property (nonatomic,copy) NSString *identifier;
@property (nonatomic,copy) NSString *xibName;

// 簡單初始化 (單行cell)
- (ZTCoolTableCellList *)initSimpleCell:(CGFloat)cellHeight
                              buildCell:(buildCellInfo)buildCell
                              clickCell:(clickBlock)clickCell;

// 複雜初始化 - 不可刷新
- (ZTCoolTableCellList *)initComplexCellNoRefresh:(CGFloat)headHeigth
                                        buildHead:(buildCell)buildHead
                                       footHeight:(CGFloat)footHeight
                                        buildFoot:(buildCell)buildFoot
                                       cellHeight:(CGFloat)cellHeight
                                        buildCell:(buildCellInfo)buildCell
                                        clickCell:(clickBlock)clickCell
                                        cellCount:(NSInteger)cellCount
                                       identifier:(NSString *)identifier
                                          xibName:(NSString *)xibName;

// 複雜初始化 - 可刷新
- (ZTCoolTableCellList *)initComplexCellHasRefresh:(CGFloat)headHeigth
                                         buildHead:(buildCell)buildHead
                                        footHeight:(CGFloat)footHeight
                                         buildFoot:(buildCell)buildFoot
                                        cellHeight:(CGFloat)cellHeight
                                         buildCell:(buildCellInfo)buildCell
                                         clickCell:(clickBlock)clickCell
                                       refreshCell:(refreshBlock)refreshCell
                                         cellCount:(NSInteger)cellCount
                                        identifier:(NSString *)identifier
                                           xibName:(NSString *)xibName;
@end
.h文件
@implementation ZTCoolTableViewCell

@end

@implementation ZTCoolTableCellList

// 簡單初始化
- (ZTCoolTableCellList *)initSimpleCell:(CGFloat)cellHeight
                              buildCell:(buildCellInfo)buildCell
                              clickCell:(clickBlock)clickCell{
    
    return [self initComplexCellNoRefresh:0 buildHead:nil footHeight:0 buildFoot:nil cellHeight:cellHeight buildCell:buildCell clickCell:clickCell cellCount:1 identifier:nil xibName:nil];
}

// 複雜初始化 - 不可刷新
- (ZTCoolTableCellList *)initComplexCellNoRefresh:(CGFloat)headHeigth
                                        buildHead:(buildCell)buildHead
                                       footHeight:(CGFloat)footHeight
                                        buildFoot:(buildCell)buildFoot
                                       cellHeight:(CGFloat)cellHeight
                                        buildCell:(buildCellInfo)buildCell
                                        clickCell:(clickBlock)clickCell
                                        cellCount:(NSInteger)cellCount
                                       identifier:(NSString *)identifier
                                          xibName:(NSString *)xibName{
    
    if(headHeigth >0){
        self.headCell = [[ZTCoolTableViewCell alloc] init];
        self.headCell.height = headHeigth;
        self.headCell.buildCell = buildHead;
    }
    
    if(footHeight >0){
        self.footCell = [[ZTCoolTableViewCell alloc] init];
        self.footCell.height = footHeight;
        self.footCell.buildCell = buildFoot;
    }
    
    self.cellHeigth = cellHeight;
    self.buildCellInfo = buildCell;
    self.clickBlock = clickCell;
    self.cellCount = cellCount;
    self.identifier = identifier;
    self.xibName = xibName;
    
    return self;
}

// 複雜初始化 - 可刷新
- (ZTCoolTableCellList *)initComplexCellHasRefresh:(CGFloat)headHeigth
                                         buildHead:(buildCell)buildHead
                                        footHeight:(CGFloat)footHeight
                                         buildFoot:(buildCell)buildFoot
                                        cellHeight:(CGFloat)cellHeight
                                         buildCell:(buildCellInfo)buildCell
                                         clickCell:(clickBlock)clickCell
                                       refreshCell:(refreshBlock)refreshCell
                                         cellCount:(NSInteger)cellCount
                                        identifier:(NSString *)identifier
                                           xibName:(NSString *)xibName{
    
    if(headHeigth >0){
        self.headCell = [[ZTCoolTableViewCell alloc] init];
        self.headCell.height = headHeigth;
        self.headCell.buildCell = buildHead;
    }
    
    if(footHeight >0){
        self.footCell = [[ZTCoolTableViewCell alloc] init];
        self.footCell.height = footHeight;
        self.footCell.buildCell = buildFoot;
    }
    
    self.cellHeigth = cellHeight;
    self.buildCellInfo = buildCell;
    self.clickBlock = clickCell;
    
    if(refreshCell){
        self.refreshBlock = refreshCell;
    }
    
    self.cellCount = cellCount;
    self.identifier = identifier;
    self.xibName = xibName;
    
    return self;
}
.m文件

二、讓UITableView自己做自己的dataSource和delegate -- ZTCoolTableViewBase

@class ZTCoolTableCellList;

@interface ZTCoolTableViewBase : UITableView <UITableViewDataSource, UITableViewDelegate>

// UITableView的數據集合
@property (nonatomic,strong) NSMutableArray<ZTCoolTableCellList*> *arrayTableViewCellList;

@end
.h文件
@implementation ZTCoolTableViewBase

#pragma mark-hitTest
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
    id view = [super hitTest:point withEvent:event];
    if(![view isKindOfClass:[UITextField class]]){
        [self endEditing:YES];
    }
    return view;
}

#pragma mark - TableViewDelegate
// section頭部高度
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
    if(cellList.headCell){
        return cellList.headCell.height;
    }else{
        return 0.00001;
    }
}

// section底部高度
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
    ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
    if(cellList.footCell){
        return  cellList.footCell.height;
    }else{
        return 0.00001;
    }
}

// 有多少section
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    return [self.arrayTableViewCellList count];
}

// 改變行的高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:[indexPath section]];
    if(cellList.cellHeigth == 0){
        UITableViewCell *cell = [self tableView:self cellForRowAtIndexPath:indexPath];
        return cell.frame.size.height;
    }else{
        return cellList.cellHeigth;
    }
}

// 每個section有多少行
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
    return cellList.cellCount;
}

// 頭部
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section{
    ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
    if(cellList.headCell.buildCell){
        return cellList.headCell.buildCell(tableView,section);
    }else{
        return nil;
    }
}

// cell數據構造
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:[indexPath section]];
    return cellList.buildCellInfo(tableView,indexPath);
}

// 底部
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section{
    ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:section];
    if(cellList.footCell.buildCell){
        return cellList.footCell.buildCell(tableView,section);
    }else{
        return nil;
    }
}

// 選中某個項
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    ZTCoolTableCellList *cellList = [self.arrayTableViewCellList objectAtIndex:[indexPath section]];
    if(cellList.clickBlock){
        return cellList.clickBlock(tableView,indexPath);
    }
}

- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0){
    return YES;
}
.m文件

如此,我們便實現了UITableViewCell的對象化封裝和Controller於UITableView數據源及代理的耦合。

那麼如何實際運用呢?我們來舉個例子,如下圖,實現這樣一個頁面:

 

按照以前的思維,將控制器充當UITableView的dataSource和delegate,那麼就會出現

_tableView.delegate = (id)self;

_tableView.dataSource = (id)self;

 而且每個Controller頁面都是實現的協議代理方法,一長串的重覆代碼!!!

 那麼現在有了新需求,需要動態的再第一個section和第二個section之間新增一個section,包括兩行row,這就需要重新代碼佈局,涉及到了所有     row點擊事件極有可能需要重新綁定section與row值,對於能躺著絕對不站著的懶程式猿來說,這簡直不要太扎心!如果使用上面封裝的設計去實現,簡直不要太舒服!

一、聲明對象

// 主界面容器UITableView
@property (nonatomic,strong) ZTCoolTableViewBase   *tableView;
// 第一個section(個人資料、我的錢包)
@property (nonatomic,strong) ZTCoolTableCellList   *firstCell;
// 第二個section(交易記錄、聯繫客服、設置)
@property (nonatomic,strong) ZTCoolTableCellList   *secondCell;
// 第三個section(私人日記、統計面板)
@property (nonatomic,strong) ZTCoolTableCellList   *thirdCell;

二、設置UITableView數據源和代理

- (ZTCoolTableViewBase *)tableView{
    if (!_tableView) {
        CGRect rect = [UIScreen mainScreen].bounds;
        _tableView = [[ZTCoolTableViewBase alloc] initWithFrame:rect style:UITableViewStyleGrouped];
        _tableView.arrayTableViewCellList = [[NSMutableArray alloc] initWithObjects:
                                             self.firstCell,
                                             self.thirdCell,
                                             nil];
        _tableView.delegate = _tableView;
        _tableView.dataSource = _tableView;
        _tableView.sectionHeaderHeight = 0;
        _tableView.separatorColor = [UIColor groupTableViewBackgroundColor];
    }
    return _tableView;
}

其中:

// 設置UITableView的代理為自己  
 _tableView.delegate = _tableView;
// 設置UITableView的數據源為自己 
_tableView.dataSource = _tableView;

// 初始化UITableView的數據對象集合
 _tableView.arrayTableViewCellList = [[NSMutableArray alloc] initWithObjects:
                                             self.firstCell,
                                             self.thirdCell,
                                             nil];

 三、懶載入數據集合

#pragma mark - firstCell
- (ZTCoolTableCellList *)firstCell{
    if (!_firstCell) {
        BIWeakObj(self)
        static NSString *identifier = @"firstCell";
        _firstCell = [[ZTCoolTableCellList alloc] init];
        _firstCell = [_firstCell initComplexCellNoRefresh:0 buildHead:nil footHeight:10 buildFoot:nil cellHeight:44 buildCell:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) {
            
            UITableViewCell *cell = [selfWeak.tableView dequeueReusableCellWithIdentifier:identifier];
            
            if(cell == nil){
                cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
                cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
                cell.textLabel.font = [UIFont systemFontOfSize:14.0f];
                
                if(indexPath.row == 0){
                    cell.imageView.image = [UIImage imageNamed:@"ic_my_info"];
                    cell.textLabel.text = @"個人資料";
                }
                else{
                    cell.imageView.image = [UIImage imageNamed:@"ic_my_money"];
                    cell.textLabel.text = @"我的錢包";
                }
            }
            
            return cell;
            
        } clickCell:^(UITableView *tableView, NSIndexPath *indexPath) {
            
            [selfWeak clickCell:indexPath];
            
        } cellCount:2 identifier:identifier xibName:nil];
    }
    return _firstCell;
}
firstCell
#pragma mark - secondCell
- (ZTCoolTableCellList *)secondCell{
    if (!_secondCell) {
        BIWeakObj(self)
        static NSString *identifier = @"secondCell";
        _secondCell = [[ZTCoolTableCellList alloc] init];
        _secondCell = [_secondCell initComplexCellNoRefresh:0 buildHead:nil footHeight:10 buildFoot:nil cellHeight:44 buildCell:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) {
            
            UITableViewCell *cell = [selfWeak.tableView dequeueReusableCellWithIdentifier:identifier];
            
            if(cell == nil){
                cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
                cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
                cell.textLabel.font = [UIFont systemFontOfSize:14.0f];
                
                if(indexPath.row == 0){
                    cell.imageView.image = [UIImage imageNamed:@"ic_my_log"];
                    cell.textLabel.text = @"私人日記";
                }
                else{
                    cell.imageView.image = [UIImage imageNamed:@"ic_my_statistic"];
                    cell.textLabel.text = @"統計面板";
                }
            }
            
            return cell;
            
        } clickCell:^(UITableView *tableView, NSIndexPath *indexPath) {
            
            [selfWeak clickCell:indexPath];
            
        } cellCount:2 identifier:identifier xibName:nil];
    }
    return _secondCell;
}
secondCell
#pragma mark - thirdCell
- (ZTCoolTableCellList *)thirdCell{
    if (!_thirdCell) {
        BIWeakObj(self)
        static NSString *identifier = @"thirdCell";
        _thirdCell = [[ZTCoolTableCellList alloc] init];
        
        _thirdCell = [_thirdCell initComplexCellHasRefresh:0 buildHead:nil footHeight:0 buildFoot:nil cellHeight:44 buildCell:^UITableViewCell *(UITableView *tableView, NSIndexPath *indexPath) {
            
            UITableViewCell *cell = [selfWeak.tableView dequeueReusableCellWithIdentifier:identifier];
            
            if(cell == nil){
                cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier];
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
                cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
                cell.textLabel.font = [UIFont systemFontOfSize:14.0f];
                
                if(indexPath.row == 0){
                    cell.imageView.image = [UIImage imageNamed:@"ic_my_quotebill"];
                    cell.textLabel.text = @"交易記錄";
                }
                else if(indexPath.row == 1){
                    cell.imageView.image = [UIImage imageNamed:@"ic_my_service"];
                    cell.textLabel.text = @"聯繫客服";
                }
                else{
                    cell.imageView.image = [UIImage imageNamed:@"ic_my_setup"];
                    cell.textLabel.text = @"設置";
                }
            }
            
            return cell;
            
        } clickCell:^(UITableView *tableView, NSIndexPath *indexPath) {
            
            [selfWeak clickCell:indexPath];
            
        } refreshCell:^{
            
            [selfWeak.tableView.arrayTableViewCellList insertObject:selfWeak.secondCell atIndex:1];
            [selfWeak.tableView reloadData];
            
        } cellCount:3 identifier:identifier xibName:nil];
    }
    return _thirdCell;
}
thirdCell

其中第三個cell可刷新(為了給第二個cell指定新增時的入口)這裡是個block:

refreshCell:^{
            
            [selfWeak.tableView.arrayTableViewCellList insertObject:selfWeak.secondCell atIndex:1];
            [selfWeak.tableView reloadData];
            
        }

新增按鈕點擊事件:

- (void)addTableviewSection:(id)sender{
    if(self.thirdCell.refreshBlock){
        self.thirdCell.refreshBlock();
    }
}

如此實現,在解耦的同時還能簡化重覆代碼量,並且可以最小的代價cost適應頻繁變化的UI設計!

 

PS:目前的封裝只支持每個section塊的每行row高度是一樣的,如果存在不一致的需求,可在我的基礎上進行二次封裝變化,如果我的文章對您有些許幫助,幫忙點贊標星,如需轉載,請說明出處,謝謝!

demo  Github地址:https://github.com/BeckWang0912/ZTCoolTableView  喜歡就標個星星吧✨✨~~~✨✨^o^

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 移動頁面經常會做到這樣一個效果,看下麵的圖片,重點就是那個固定定位的底部,裡面有個input輸入框,在輸入字元的時候,安卓手機看著是完全沒有問題! 1、頁面沒有點擊底部input的初始狀態 2、安卓手機下的樣子,一切都是那麼的正常,簡直是完美。 3、ios手機下,就是這麼任性,沒有辦法,完全擋著了, ...
  • 數據綁定的方法: 1、利用動態創建元素節點和把它追加到頁面中的方式實現數據綁定(將動態添加的li添加到ul中) 優勢:把需要動態綁定的內容一個個的追加到頁面中,對原來的元素沒有任何的影響 弊端:每當創建一個li,我們就添加到頁面中,引發一次DOM的迴流,最後引發迴流次數過多,影響我們的性能。 2、字 ...
  • 目錄: 一、介紹 二、node安裝 三、webstorm配置node環境 四、代碼介紹 五、如何使用 六、自定義功能變數名稱 七、其他 一、介紹 1、背景 日常工作中,跟後端商定好介面格式後;通常採用的開發方式,就是自己新建一個json文件,手動的模擬一批數據,進行ajax調用。 但是如果遇到後端提供的介面 ...
  • 浮動 會使當前標簽產生上浮效果,從而導致父標簽高度塌陷的問題 1. 給父元素指定高度 <div style="height:200px"> <div style="float:right"></div> </div> 簡單粗暴!高度不定時,如果內部高度大於父級時,容易產生問題 2. 在浮動元素後邊添 ...
  • appcan的 uexXmlHttpMgr.send 或者 appcan.ajax無法同步請求(沒有找到這個屬性),只能非同步,造成迴圈多次提交時由於延遲或網路堵塞等原因無法同步響應,導致提交順序混亂,執行完後回調錯誤或丟數據,如傳統方法(這裡已經引用的JQ包) 1 var data=[]; 2 va ...
  • 編寫存儲過程分頁(此處使用T-SQL) 1 CREATE PROC [dbo].[Common_PageList] 2 ( 3 @tab nvarchar(max), 表名 4 @strFld nvarchar(max), --欄位字元串 5 @strWhere varchar(max), --wh ...
  • https://github.com/saki4510t/UVCCamera UVCCamera 聽名字就知道使用UVC( USB VEDIO CLASS) 協議的通用類庫。linux原生支持,基本支持市面上所有免區USB攝像頭。 此開源庫包含JNI及android封裝的類庫。是目前最好用的安卓US... ...
  • 反編譯apk,在smali中註入一段自己的代碼。 試了幾個工具(apkdb、apktool、apkSign), 發現反編譯都可以,但是回編譯都不相容java1.8,導致回編譯成功,但apk沒有簽名,不能安裝; 在網上找了一些資料,都不行; 無奈只能換回java1.7,實測成功。 如果哪位有更好的方法 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...