对于iOS开发者,UITableViewCell的重用是最基本的技能,初学者都应该掌握的。对于它的原理我就不在此啰嗦了,这里我重点说下,如何以正确的姿态来重用多类型的UITableViewCell,正确重用cell不仅仅要重用cell视图,还需要好好重用cell的子视图。你是否做到了呢?
单一类型cell重用
对于简单单一cell的tableView来讲,它的重用我们大多会在代理方法里这样写:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//设置重用标识符
static NSString * Identifier = @"MineCell";
//通过Identifier取到cell
MineTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:Identifier];
//若cell不存在,在进行创建
if (!cell) {
cell =[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: Identifier];
}
//用set方法取到对应的model对cell赋值
[cell cellModel:self.cellModelArr[indexPath.row]];
return cell;
}
或者使用需要注册cell的写法:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//设置重用标识符
static NSString *Identifier = @"MineCell";
MineTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier forIndexPath:indexPath];
//用set方法取到对应的model对cell赋值
[cell setCellModel:self.cellModelArr[indexPath.row]];
return cell;
}
上面的两种写法都很简单,由于是单一类型的cell重用,不会涉及到子视图的重用,所以没什么可讲的。
多类型cell重用
在很多情况下,UITableView并不是单一的一种cell,而会包含多种cell,每种cell类型样式布局都有明显区别。这样来我们就不能用上面单一的cell重用方式了。 对于多类型cell你当然可以对每个类型的cell创建对应的.h .m文件,并在使用时引入该 cell的头文件即可。但可能是出于偷懒,我更习惯统一处理这些cell。把这些cell都写在一个.h .m文件内,通过对不同类型cell设置对应对cellStyle枚举状态来辨别他们,这样以来,一个文件就足够了。
下面就重点说下单文件下多类型cell的重用。主要代码如下:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * MineCellIdentifier = @"MineCellIdentifier";
//通过注册的cell获取MineTableViewCell
MineTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MineCellIdentifier forIndexPath:indexPath];
//根据不同row或者也可以根据model内容来区分cellStyle
if (indexPath.row == 0) {
//通过setMineCellStyle:方法对cell就行类型赋值,我们会在 setMineCellStyle:方法里对cell就行布局等定制化处理。
cell.mineCellStyle = MineTableViewCellStyleOne;
}else
{
cell.mineCellStyle = MineTableViewCellStyleTow;
}
//用set方法取到对应的model对cell赋值
[cell setCellModel:self.cellModelArr[indexPath.row]];
return cell;
}
我们一般会在MineTableViewCell.h里定义类似MineTableViewCellStyle的枚举类型,并实现mineCellStyle属性,这样就可以在对应的tableView代理方法里通过setMineCellStyle:方法来针对不同cellStyle布局了。setMineCellStyle:方法内部实现大概如下:
- (void)setMineCellStyle:(MineTableViewCellStyle)mineCellStyle
{
_mineCellStyle = mineCellStyle;
//为了避免cell重用引起的子视图错乱,我们会先把cell的子视图给全不移除,下面会在对应的cellStyle内重新创建并添加到cell的contentView内。
for (UIView *view in self.contentView.subviews) {
[view removeFromSuperview];
}
//对不同的类型进行针对性的布局绘制操作
switch (mineCellStyle) {
case MineTableViewCellStyleOne:
{
//这里省略了布局约束的代码
NSArray *viewArray = @[self.bankNumTitleLb, self.bankNumLb];
for (UIView *view in viewArray) {
[self.contentView addSubview:view];
}
//进行布局操作(此处省略不是本文重点)
}
break;
case MineTableViewCellStyleTow:
{
//这里省略了布局约束的代码
NSArray *viewArray = @[self.phoneNumTitleLb, self.phoneNumTextField];
for (UIView *view in viewArray) {
[self.contentView addSubview:view];
}
//进行布局操作(此处省略不是本文重点)
}
break;
case MineTableViewCellStyleDefault:
{
//这里省略了布局约束的代码
NSArray *viewArray = @[self.verificationCodeTitleLb,
self.phoneNumTextField,self.verificationCodeButton];
for (UIView *view in viewArray) {
[self.contentView addSubview:view];
}
//进行布局操作(此处省略不是本文重点)
}
break;
}
}
到这里也许你觉得一切都没什么问题。但有经验的开发者可能已经看出来问题所在。问题出在setMineCellStyle:方法里的这句代码:
for (UIView *view in self.contentView.subviews) {
[view removeFromSuperview];
}
这句代码很简单,就是前面说到为了解决cell重用时会出现子视图的重用。可是"解决"了子视图的重用问题,那么新问题来了,每次都把子视图移除,重新创建既消耗内存还占用时间,严重会出现滑动出现卡顿现象,而且都删除了重建还能叫重用吗?最多是只是留了个cell的'壳',里面的'肉'可都是新建的啊。
因此:在通过设置多种cell枚举类型来实现多样式cell布局时,要先判断当前重用的cellStyle 和 需要设置的cellStyle是否一致,不一致时,才需要重新布局绘制。
修改后的主要代码如下:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString * MineCellIdentifier = @"MineCellIdentifier";
UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:MineCellOneIdentifier forIndexPath:indexPath];
//获取当前重用cell的类型cellStyle
MineCellStyle cellStyle = cell.mineCellStyle;
switch (indexPath.section) {
case 0:
{
//如果当前重用的cellStyle 和 需要设置的cellStyle不一致时,才进行类型重制!
if (cellStyle != MineTableViewCellStyleOne) {
cell.mineCellStyle = MineTableViewCellStyleOne;
}
[cell setCellModel:self.cellModelArr[indexPath.row]];
}
break;
case 1:
{
switch (indexPath.row) {
case 0:
{
if (cellStyle != MineTableViewCellStyleTow) {
cell.mineCellStyle = MineTableViewCellStyleTow;
}
[cell setCellModel:self.cellModelArr[indexPath.row]];
}
break;
case 1:
{
if (cellStyle != MineTableViewCellStyleThree) {
cell.mineCellStyle = MineTableViewCellStyleThree;
}
[cell setCellModel:self.cellModelArr[indexPath.row]];
}
break;
}
}
break;
}
return cell;
}
这样以来即实现了单文件下实现多cell类型的功能,又完美的解决了多cell的重用及性能问题,如有疏漏不足之处多多指出。