对于iOS开发者,UITableViewCell的重用是最基本的技能,初学者都应该掌握的。对于它的原理我就不在此啰嗦了,这里我重点说下,如何以正确的姿态来重用多类型的UITableViewCell,正确重用cell不仅仅要重用cell视图,还需要好好重用cell的子视图。你是否做到了呢?
单一类型cell重用
对于简单单一cell的tableView来讲,它的重用我们大多会在代理方法里这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13
| - (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的写法:
1 2 3 4 5 6 7 8
| - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { //设置重用标识符 static NSString *Identifier = @"MineCell"; MineTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier forIndexPath:indexPath]; //通过协议绑定VM和cell cell.bind(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的重用。主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| - (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; } //通过协议绑定VM和cell cell.bind(self.cellModelArr[indexPath.row]); return cell; }
|
我们一般会在MineTableViewCell.h里定义类似MineTableViewCellStyle的枚举类型,并实现mineCellStyle属性,这样就可以在对应的tableView代理方法里通过setMineCellStyle:方法来针对不同cellStyle布局了。setMineCellStyle:方法内部实现大概如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| - (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:方法里的这句代码:
1 2 3
| for (UIView *view in self.contentView.subviews) { [view removeFromSuperview]; }
|
这句代码很简单,就是前面说到为了解决cell重用时会出现子视图的重用。可是”解决”了子视图的重用问题,那么新问题来了,每次都把子视图移除,重新创建既消耗内存还占用时间,严重会出现滑动出现卡顿现象,而且都删除了重建还能叫重用吗?最多是只是留了个cell的’壳’,里面的’肉’可都是新建的啊。
因此:在通过设置多种CELL枚举类型来实现多样式CELL布局时,要先判断当前重用的CELLSTYLE 和 需要设置的CELLSTYLE是否一致,不一致时,才需要重新布局绘制。
修改后的主要代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| - (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.bind(self.cellModelArr[indexPath.row]); } break; case 1: { switch (indexPath.row) { case 0: { if (cellStyle != MineTableViewCellStyleTow) { cell.mineCellStyle = MineTableViewCellStyleTow; } cell.bind(self.cellModelArr[indexPath.row]); } break; case 1: { if (cellStyle != MineTableViewCellStyleThree) { cell.mineCellStyle = MineTableViewCellStyleThree; } cell.bind(self.cellModelArr[indexPath.row]); } break; } } break; } return cell; }
|
这样以来即实现了单文件下实现多cell类型的功能,又完美的解决了多cell的重用及性能问题。但是如果cell比较复杂,后续业务变动大还是建议不同cell单独定义,方便后续拓展维护。