UITableView“優(yōu)雅”支持不同類型的Cell

在某些業(yè)務場景下,同一個UITableView需要支持多種UITableViewCell??紤]一下即時通信的聊天對話列表,不同的消息類型對應于不同的UITableViewCell,同一個tableview往往需要支持多達10種以上的cell類型,例如文本、圖片、位置、紅包等等。一般情況下,UITableViewCell往往會和業(yè)務數(shù)據(jù)模型綁定,來展示數(shù)據(jù)。根據(jù)不同的業(yè)務數(shù)據(jù),對應不同的cell。本文將探討如何有效的管理和加載多種類型的cell。

為了方便討論,假設我們要實現(xiàn)一個員工管理系統(tǒng)。一個員工包含名字和頭像。如果員工只有名字,則只展示名字,如果只有頭像,則只展示頭像,如果既有名字,又有頭像,則需要既展示頭像又展示名字。

我們用一個Person類表示員工,用三種不同的cell來處理不同的展示情況。

@interface?Person?:?NSObject

@property?(nonatomic,?copy)?NSString?*name;

@property?(nonatomic,?strong)?NSString?*avatar;

@end

/*負責展示只有名字的員工*/

@interface?TextCell?:?BaseCell

-?(void)setPerson:(Person?*)p;

@end

/*負責展示只有頭像的員工*/

@interface?ImageCell?:?BaseCell

-?(void)setPerson:(Person?*)p;

@end

/*負責展示只有既有名字又有頭像的員工*/

@interface?TextImageCell?:?BaseCell

-?(void)setPerson:(Person?*)p;

@end

這三個類都繼承了BaseCell,BaseCell繼承UITableViewCell

@interface?BaseCell?:?UITableViewCell

-?(void)setPerson:(Person?*)p;

@end

下面我們在UITableView的delegate來處理展示Cell

第一次嘗試:

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

{

BaseCell?*cell;

NSString?*cellIdentifier;

switch?(p.showtype)?{

case?PersonShowText:

cellIdentifier?=?@"TextCell";

break;

case?PersonShowAvatar:

cellIdentifier?=?@"PersonShowAvatar";

break;

case?PersonShowTextAndAvatar:

cellIdentifier?=?@"PersonShowTextAndAvatar";

break;

default:

break;

}

cell?=?[tableView?dequeueReusableCellWithIdentifier:cellIdentifier];

if?(!cell)?{

switch?(p.showtype)?{

case?PersonShowText:

cell?=?[[TextCell?alloc]?initWithStyle:UITableViewCellStyleDefault?reuseIdentifier:cellIdentifier];

break;

case?PersonShowAvatar:

cell?=?[[ImageCell?alloc]?initWithStyle:UITableViewCellStyleDefault?reuseIdentifier:cellIdentifier];

break;

case?PersonShowTextAndAvatar:

cell?=?[[TextImageCell?alloc]?initWithStyle:UITableViewCellStyleDefault?reuseIdentifier:cellIdentifier];

break;

default:

break;

}

}

[cell?setPerson:p];

return?cell;

}

這段代碼實現(xiàn)了根據(jù)不同的業(yè)務模型選取和顯示Cell的邏輯。但是這段代碼包含了重復代碼,switch case被調用了兩次。我們改進一下代碼:

第二次嘗試

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

{

BaseCell?*cell;

NSString?*cellIdentifier;

Class?cellClass;

switch?(p.showtype)?{

case?PersonShowText:

cellClass?=?[TextCell?class];

break;

case?PersonShowAvatar:

cellClass?=?[ImageCell?class];

break;

case?PersonShowTextAndAvatar:

cellClass?=?[TextImageCell?class];

break;

default:

cellClass?=?[UITableViewCell?class];

break;

}

cellIdentifier?=?NSStringFromClass(cellClass);

cell?=?[tableView?dequeueReusableCellWithIdentifier:cellIdentifier];

if?(!cell)?{

cell?=?[[cellClass?alloc]?initWithStyle:UITableViewCellStyleDefault?reuseIdentifier:cellIdentifier];

}

[cell?setPerson:p];

return?cell;

}

這次比第一次的代碼看起來好了不少,通過一個通用的Class對象,來動態(tài)生成cell,避免了兩次調用switch case的重復代碼。但是,有沒有更好的實現(xiàn)方式?

第三次嘗試

-?(void)viewDidLoad

{

...

[self?registerCell];??//注冊cell

}

-?(void)registerCell

{

[_tableView?registerClass:[TextCell?class]?forCellReuseIdentifier:NSStringFromClass([TextCell?class])];

[_tableView?registerClass:[ImageCell?class]?forCellReuseIdentifier:NSStringFromClass([ImageCell?class])];

[_tableView?registerClass:[TextImageCell?class]?forCellReuseIdentifier:NSStringFromClass([TextImageCell?class])];

}

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

{

Person?*p?=?_persons[indexPath.row];

BaseCell?*cell;

NSString?*cellIdentifier;

switch?(p.showtype)?{

case?PersonShowText:

cellIdentifier?=?NSStringFromClass([TextCell?class]);

break;

case?PersonShowAvatar:

cellIdentifier?=?NSStringFromClass([ImageCell?class]);

break;

case?PersonShowTextAndAvatar:

cellIdentifier?=?NSStringFromClass([TextImageCell?class]);

break;

default:

cellIdentifier?=?NSStringFromClass([UITableViewCell?class]);

break;

}

cell?=?[tableView?dequeueReusableCellWithIdentifier:cellIdentifier?forIndexPath:indexPath];

[cell?setPerson:p];

return?cell;

}

可以看到,這次我們調用了- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier方法,把tableView和cell先配置好,并且在cellForRowAtIndexPath方法里面,去掉了if (!cell) {...}的處理,代碼看起來更加簡潔。

為什么不再需要判斷cell是否為空?因為通過registerClass方法注冊了cell之后,dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath方法會確保有一個可用的cell返回。

當然,我們可以把類型判斷的這段代碼提取出來,讓cellForRowAtIndexPath方法看起來更加簡潔

@interface?Person?:?NSObject

......

@property?(nonatomic,?strong)?NSString?*cellIdentifier;

@end

@implementation?Person

-?(NSString?*)cellIdentifier

{

if?(_showtype?==?PersonShowTextAndAvatar)?{

return?NSStringFromClass([TextImageCell?class]);

}?else?if?(_showtype?==?PersonShowAvatar){

return?NSStringFromClass([ImageCell?class]);

}?else?{

return?NSStringFromClass([TextCell?class]);

}

}

@end

現(xiàn)在cellForRowAtIndexPath方法看起來就像下面這樣,明顯簡潔多了

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

{

Person?*p?=?_persons[indexPath.row];

BaseCell?*cell;

NSString?*cellIdentifier;

cellIdentifier?=?p.cellIdentifier;

cell?=?[tableView?dequeueReusableCellWithIdentifier:cellIdentifier?forIndexPath:indexPath];

[cell?setPerson:p];

return?cell;

}

結論:

使用- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier和- (UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath可以讓UITableView處理多種類型的cell更加靈活和輕松。

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容