iOS多種類型的cell處理方案

在項目開發(fā)中UITableView和UICollectionView應(yīng)該是最長用的控件了吧,而這兩種控件的核心是cell的處理和展示。隨著App的發(fā)展和需求的不斷累加,頁面是單一cell的情況越來越少,更多的是各種復(fù)雜cell的組合。常見的比如App的首頁


app首頁示例圖

那么像這種頁面我們是如何處理cell的呢?

1.最常見的也是很多人會不經(jīng)思考的,直接根據(jù)indexPath一一對應(yīng),寫出下面的代碼:

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

? ? if(indexPath.section==0) {

? ? }

}

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

? ? [tableViewdeselectRowAtIndexPath:indexPath animated:YES];

? ? if(indexPath.section==0) {

? ?}
}

雖然這種在開發(fā)階段很容易,但是在后期的二次開發(fā)和維護(hù)上改一個地方tableview的delegate和datasource的方法都需要改,成本很高。而且cellForRowAtIndexPath的方法里面是清一色的if-else,然后是做了各種各樣的事情,很容易造成代碼的臃腫,動不動就是幾十行或者幾百行代碼,不利于閱讀和重用。

這種方案的缺點(diǎn)有以下幾點(diǎn):

1.一般情況下項目中不建議出現(xiàn)0、1等具體的數(shù)字,因為它除了表示位置之外,毫無其他意義。

2.容易出錯,在cell代理方法,高度代理方法,點(diǎn)擊代理方法里面要保持一致,容易出錯。

3.不方便修改,如果要修改兩個cell的順序或者添加修改,要修改好幾個地方,改動太大。

2.根據(jù)model來對應(yīng)cell,cell面向model開發(fā)

前面提到了不因該出現(xiàn)indexPath等具體的位置數(shù)字,對于一個tableview,位置數(shù)字肯定是有的,我們要消除數(shù)字,那就得找到相應(yīng)的數(shù)據(jù)來代替它。這里,主要的場景一般都是一個類型的數(shù)據(jù)(model)對應(yīng)一種類型的cell,所以類型是固定的,所以我們用一個枚舉來定義所有類型的cell

typedefNS_ENUM(NSInteger, HomeCellType) {

?HomeCellTypeOne =0,

?HomeCellTypeTwo?

?HomeCellTypeThree,?

?HomeCellTypeFourl,?

?};

然后在cellForRow方法,根據(jù)model類型加載對應(yīng)的cell,例如:

? ?id model = self.viewModel.dataArray[indexPath.row];

switch([self?getHomeCellType] ){

? caseHomeCellTypeOne:

?HomeCellOne?*cell = [collectionView dequeueReusableCellWithReuseIdentifier:[HomeCellOne cellIdentifier] forIndexPath:indexPath];

? ?breke;

caseHomeCellTypeOne:

?HomeCellTwo *cell = [collectionView dequeueReusableCellWithReuseIdentifier:[HomeCellTwo?cellIdentifier] forIndexPath:indexPath];

? ?breke;

? ?....

}

?- (HomeCellType)getHomeCellType:(id)model {

? ? ? ? HomeCellType type = HomeCellTypeOne;

? ? ? ? if([model isKindOfClass:[HomeCellTypeOneModel class]]) {

? ? ? ? ? ? type = HomeCellTypeOne;

? ? ? ? }else if([model isKindOfClass:[HomeCellTypeTwoModel class]]) {

? ? ? ? ? ? type = HomeCellTypeTwo;

? ? ? ? }else if(){

? ? ? ? }

? ? ? ...

}

這樣看到了cellType或者model就知道如何去處理相應(yīng)的cell了,清晰易理解。而且如果想復(fù)用、刪除、添加、改動順序,只需要改動數(shù)據(jù)源即可,其他不需要動,改動量很小。但是這樣寫的還不是很好,cell和datasource的cellForRowAtIndexPath耦合的還有點(diǎn)嚴(yán)重。那如果其他的地方只是用到了部分cell類型,我們還需要把上面的代碼再copy一份?或者說我想讓cell根據(jù)model去自動選擇cell類型,而不是import各種cell。頭文件,在cellForRowAtIndexPath方法里面判斷,不依賴具體的cell呢?

那么我的面向協(xié)議開發(fā)的設(shè)計模式就上場了。就是讓model繼承一個協(xié)議,該協(xié)議實現(xiàn)了cell的一些方法,例如cell的復(fù)用標(biāo)示、cell的類型、cell的高度、cell的點(diǎn)擊事件等。

改進(jìn)版

1.定義協(xié)議接口

@protocol ModelConfigProtocol

@optional

/**

獲取 cell 的復(fù)用標(biāo)識

@return 復(fù)用標(biāo)識

*/

- (nullableNSString*)cellReuseIdentifier;

/**

獲取 cell 的類型

@return cell 的類型

*/

- (cellType)cellType;

/**

獲取 cell 的高度

@param indexPath indexPath

@return 高度

*/

- (CGFloat)cellHeightWithindexPath:(NSIndexPath*)indexPath;

/**

cell 點(diǎn)擊

@param indexPath indexPath

@param other 其它對象

*/

- (void)cellDidSelectRowAtIndexPath:(NSIndexPath*)indexPath other:(_Nullable id)other;

2.然后實現(xiàn)實現(xiàn)該協(xié)議接口。定義一個抽象類的model遵守該協(xié)議實現(xiàn)協(xié)議

@interface BaseModel : NSObject<ModelConfigProtocol>

@end

@implementation BaseModel

- (cellType)cellType{

? ? return0;

}

- (NSString*)cellReuseIdentifier{

? ? return@"";

}

- (CGFloat)cellHeightWithindexPath:(NSIndexPath*)indexPath{

? ? return0.0;

}

- (void)cellDidSelectRowAtIndexPath:(NSIndexPath*)indexPath other:(_Nullable id)other{

? ? return;

}

@end

3.具體的model繼承自BaseModel,然后子類model具體實現(xiàn)ModelConfigProtocol的協(xié)議方法

4.定義的一個抽象類的cell,開放賦值的接口

@interfaceBaseCell : UITableViewCell

@property (nonatomic,strong) id<ModelConfigProtocol> model;

@end

@implementation BaseCell

- (void)setModel:(id)model{

}

@end

5.具體的cell繼承自BaseCell,然后子類cell具體實現(xiàn)setModel方法

6.TableView代理里數(shù)據(jù)返回

#pragma mark ---- UITableViewDelegate ----

- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath

{

id<ModelConfigProtocol>?model =self.listArray[indexPath.row];

return [model cellHeightWithindexPath:indexPath];

}

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

{

[tableView deselectRowAtIndexPath:indexPath animated:YES];

id<ModelConfigProtocol> mdoel =self.viewModel.dataArray[indexPath.row];

[model cellDidSelectRowAtIndexPath:indexPath other:nil];

}

#pragma mark ---- UITableViewDataSource ----

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

{

return1;

}

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

return self.viewModel.dataArray.count;

}

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

{

id<ModelConfigProtocol>?model =self.viewModel.dataArray[indexPath.row];

? ? BaseCell *cell = [tableView dequeueReusableCellWithIdentifier:[model cellReuseIdentifier]];

? ? cell.cellConfig = model;

returncell;

}

一般的一種類型的cell對應(yīng)一種model,如果你想一種model對應(yīng)多種cell,例如微信消息,有文本消息、圖片消息、語音消息、紅包消息、視頻消息等。你可以在具體model的cellType再做一層判斷。最厲害的地方在于可以和MVVM、適配器無縫對接,寫一個BaseViewController實現(xiàn)這些,然后其他ViewController繼承它,只需改變數(shù)據(jù)源,即可實現(xiàn)用最少的代碼實現(xiàn)復(fù)雜的頁面展示。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容