ios开发之解决重用TableViewCell导致的界面错乱的问题

  无论是初学者,又或者是老鸟,只要是学习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导致的界面错乱的问题了。

  以上四种方式,有自己的拙见,也有借鉴网上各位大牛的方法,希望这篇文章能够对大家有帮助,有什么问题可以留言,我定将知无不言,言无不尽....

posted @ 2015-11-15 10:56  遇见远洋  阅读(7473)  评论(0编辑  收藏  举报