• 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导致的界面错乱的问题了。

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

  • 相关阅读:
    perl 监控mysql数据库
    17.3Replication Solutions
    java.sql.SQLException: Can not issue data manipulation statements with executeQuery().
    java.sql.SQLException: Can not issue empty query.
    [2015-06-10 20:53:50
    mysqldump --flush-logs
    Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Truncated incorrect DOUBLE value: 'L
    Error Code: 1414. OUT or INOUT argument 2 for routine company.new_procedure is not a variable or NEW
    Deadlock found when trying to get lock; try restarting transaction
    java.text.ParseException: Unparseable date: "2015-06-09 hh:56:19"
  • 原文地址:https://www.cnblogs.com/YujianYuanYang/p/4966242.html
Copyright © 2020-2023  润新知