无论是初学者,又或者是老鸟,只要是学习ios的人都知道,TableView对于开发有多重要,然而我们在使用TableView,可能会遇到各种各样的问题,例如今天我想要说的 TableViewCell的重用的问题:
我们都知道,在TableView返回每一行cell的数据源方法中,我们一般会通过重用cell来达到节省内存的目的:通过为每个cell指定一个重用标识符(reuseIdentifier),即指定了单元格的种类,当cell滚出屏幕时,会将滚出屏幕的单元格放入重用的缓存池中,当某个未在屏幕上的单元格要显示的时候,就从这个缓存池中取出单元格进行重用。
但对于多变的自定义cell,有时这种重用机制会出错。我举个简单的例子,看下面的两张图片
<1>正常显示的图片
<2>界面错乱的图片
通过比较以上两张图片 明显可以看出 第二张图片的界面显示错乱,原因很简单 ,那就是在重用cell的时候出现了问题,那么我们该怎么解决界面错乱或者其他的问题呢?下面我一一为大家来介绍:
方法1 :
将获得cell的方法从- (UITableViewCell*)dequeueReusableCellWithIdentifier:(NSString*)identifier 换为-(UITableViewCell *)cellForRowAtIndexPath:(NSIndexPath *)indexPath
重用机制调用的就是dequeueReusableCellWithIdentifier这个方法,方法的意思就是“出列可重用的cell”,因而只要将它换为cellForRowAtIndexPath(只从要更新的cell的那一行取出cell),就可以不使用重用机制,因而问题就可以得到解决,虽然可能会浪费一些空间。
//下面是示例代码:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; // UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; //改为以下的方法 UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; //根据indexPath准确地取出一行,而不是从cell重用队列中取出 if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}......
上面的代码,无疑是能够解决界面错乱的问题,但如果数据很多,那就会浪费相当多的空间,不推荐使用。
方法2:
为每个cell指定不同的重用标识符(reuseIdentifier)来解决。 重用机制是根据相同的标识符来重用cell的,标识符不同的cell不能彼此重用。于是我们将每个cell的标识符都设置为不同,就可以避免不同cell重用的问题了。
怎么为每个cell都设置不同的标示符呢?其实很简单 在返回每一行cell的数据源方法中 通过为每一个cell的标示符绑定indexPath.section--indexPath.row就可以了,下面是示例代码:
//实现建立cell的具体内容
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString * ID = [NSString stringWithFormat:@"cell-ld%-ld%",[indexPath section ],[indexPath row]];
//2.到缓存池中去查找可重用标示符
HMWeiboCell * cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[HMWeiboCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
//给cell中得属性framemodel赋值
HMWeiboFrameModel * frameModel = self.weiboFrameArray[indexPath.row];
cell.weiboFrameModel = frameModel;
return cell;
}
//上面的这种方式 虽然说比第一种方式有了一定的改进,通过为每一个cell都绑定了一个不同的标识符能够使得cell与cell之间不会重用,解决了界面错乱的问题,但是从根本上来说还是没有太大的内存优化,同样的如果数据比较多还是比较浪费空间。
方法3:
第三种方式其实很简单就是删除重用cell的所有子视图,这句话什么意思呢?当我们从缓存池中取得重用的cell后,通过删除重用的cell的所有子视图,从而得到一个没有特殊格式的cell,供其他cell重用。是不是还是没懂什么意思,那我们就接着来看代码:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *ID = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID]; //出列可重用的cell
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
else
{
//删除cell的所有子视图
while ([cell.contentView.subviews lastObject] != nil)
{
[(UIView*)[cell.contentView.subviews lastObject] removeFromSuperview];//强制装换为UIView类型 ,移除所有子视图
}
}
return cell;
}
看完上面的代码是不是感觉这种方式很新颖,这种方式相比前两种方式来说 是比较完美的,既达到了重用的目的,又不会导致界面的错乱等问题,如果你也遇到了 自定义cell的时候 界面错乱不堪,不妨试试这种方式吧,你会觉得很神奇的...
方法4:
看完了前面三种方式,你是不是觉得就没有后文了呢,你错了,下面我再来说说第四种方式:
这第四种方式其实是比较笨的方法,为什么这么说呢,我们可以这么来想,既然重用cell导致界面错乱的原因是,重用cell的时候 数据没有更换,将重用cell的数据一起重用了,那么我们为什么想不到在给自定义cell设置数据的时候 通过判断每一个cell该使用什么样的数据呢 ,如果每一个cell使用的数据是不同的,那么就不会导致界面的数据或者是frame出现问题了,下面我就用简单的的例子来为大家演示:
//如果是某一个软件的会员 那么会员图标的frame设置 如果不是会员那么重新设置 这样就能够解决会员图标的数据和frame问题
if (weiboModel.vip) {//如果是会员
CGFloat vipX = CGRectGetMaxX(_nameF) + kMargin;
_vipF = CGRectMake(vipX, kMargin, kVipWidth, kVipWidth);
} else{
_vipF = CGRectZero;
}
再比如
//设置picture的frame 注意也要判断是否有picture
if (weiboModel.picture.length>0) {//有图片
CGFloat pictureY = CGRectGetMaxY(_textF) + kMargin;
_pictureF = CGRectMake(kMargin, pictureY, kPictureWidth, kPictureWidth);
//如果有图片行高就是最大的图片的y值加一个边距
self.cellHeight = CGRectGetMaxY(_pictureF) + kMargin;
}else {
_pictureF = CGRectZero;
//如果没有图片 最大的行高就是正文的最大高度加一个高度
self.cellHeight = CGRectGetMaxY(_textF) + kMargin;
} 上面这句代码的目的是 当有图片的时候 给图片设置frame 如果没有图片那么我们就设置frame为0,这样就能轻松的解决frame导致的界面错乱的问题了。
以上四种方式,有自己的拙见,也有借鉴网上各位大牛的方法,希望这篇文章能够对大家有帮助,有什么问题可以留言,我定将知无不言,言无不尽.... |