美文网首页iOS - 开发技巧设计
iOS - UITableViewCell重用机制出错的解决方案

iOS - UITableViewCell重用机制出错的解决方案

作者: SkyMing一C | 来源:发表于2017-05-06 08:00 被阅读190次

    Demo_github

    图片来自:[不白熊](http://www.jianshu.com/u/3616c0740cbd)

    TableViewCell重用机制

    UITableView继承自UIScrollview,是苹果为我们封装好的一个基于scroll的控件。

    UITableView中的cell可以有很多,一般会通过重用cell来达到节省内存的目的:并不为每个数据创建一个UITableViewCell,我们只创建屏幕可显示的最大的cell个数+1,然后去循环重复使用这些cell(通过为每个cell指定一个重用标识符(reuseIdentifier),即指定了单元格的种类。当cell滚出屏幕时,会将滚出屏幕的单元格放入重用的queue中,当某个未在屏幕上的单元格要显示的时候,就从这个queue中取出单元格进行重用)。

    // 设置单元格  indexPath :单元格当前所在位置 -- 哪个分区哪一行等
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath //UITableViewDataSource
    {
        static NSString *identifier = @"cell" ;
        //相当于从集合中找寻完全出屏幕的单元格.
        // identifier : 因为一个表视图中可能存在多种样式的单元格,咱们把相同样式的单元格放到同一个集合里面,为这个集合加标示符,当我们需要用到某种样式的单元格的时候,根据不同的标示符,从不同的集合中找寻单元格.
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier] ;
        // 如果从集合中未找到单元格,也就是集合中还没有单元格,也就是还没有单元格出屏幕,那么我们就需要创建单元格
        if (!cell)
        {
            // 创建cell的时候需要标示符(Identifier)是因为,当该cell出屏幕的时候需要根据标示符放到对应的集合中.
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"] ;
        return cell ;
    }
    

    TableViewCell重用机制避免重复显示的方法

    但对于多变的自定义cell,有时这种重用机制会出错。比如,当一个cell含有一个UIView的子类并被放在重用queue中以待重用,这时如果一个未包含任何子视图的cell要显示在屏幕上,就会取出并使用这个重用的cell显示在无任何子视图的cell中,这时候就会出错。这时候需要对取出的重用的cell做重新赋值,从而解决cell的重用机制导致cell的内容重复显示的问题。

    解决方案1:取消cell的重用机制,通过indexPath来创建cell.显示数据大时内存占用较多。🌰:

    //解决方案1:取消cell的重用机制,通过indexPath来创建cell.显示数据大时内存占用较多
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (tableView == self.classicRouteTableView) {
            //通过indexPath创建cell
            ClassicRouteTableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
            // 如果cell不存在就初始化cell
            if (!cell) {
                cell = [[ClassicRouteTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:classicRouteCell];
            }
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            if (self.classicRouteDataArray.count != 0) {
                NSMutableArray *EachRoute =[NSMutableArray arrayWithArray:[self.classicRouteDataArray[indexPath.section] EachRoute]];
                cell.EachRoute = EachRoute;
            }
            return cell;
        }
        
        if (tableView == self.hotRouteTableView) {
            //通过indexPath创建cell
            HotRouteTableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
            // 如果cell不存在就初始化cell
            if (!cell) {
                cell = [[HotRouteTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:hotRouteCell];
            }
    
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            if (self.hotRouteDataArray.count != 0) {
                NSMutableArray *EachRoute =[NSMutableArray arrayWithArray:[self.hotRouteDataArray[indexPath.section] EachRoute]];
                cell.EachRoute = EachRoute;
            }
            return cell;
        }
        return nil;
    }
    
    

    解决方案2:让每个cell都拥有一个对应的标识.显示数据大时内存占用较多。🌰:

    //解决方案2:让每个cell都拥有一个对应的标识.显示数据大时内存占用较多
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (tableView == self.classicRouteTableView) {
            // 每次先从字典中根据IndexPath取出唯一标识符
            NSString *identifier = [self.identifierDic objectForKey:[NSString stringWithFormat:@"classicRouteCell%@",indexPath]];
            // 如果取出的唯一标示符不存在,则初始化唯一标示符,并将其存入字典中,对应唯一标示符注册Cell
            if (identifier == nil) {
                identifier = [NSString stringWithFormat:@"%@", indexPath];
                [self.identifierDic setValue:identifier forKey:[NSString stringWithFormat:@"classicRouteCell%@",indexPath]];
                // 注册Cell
                [self.classicRouteTableView registerClass:[ClassicRouteTableViewCell class] forCellReuseIdentifier:identifier];
            }
            
            ClassicRouteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            if (self.classicRouteDataArray.count != 0) {
                NSMutableArray *EachRoute =[NSMutableArray arrayWithArray:[self.classicRouteDataArray[indexPath.section] EachRoute]];
                cell.EachRoute = EachRoute;
            }
            return cell;
        }
        
        if (tableView == self.hotRouteTableView) {
            // 每次先从字典中根据IndexPath取出唯一标识符
            NSString *identifier1 = [self.identifierDic objectForKey:[NSString stringWithFormat:@"hotRouteCell%ld%@",self.hotDayCount, indexPath]];
            // 如果取出的唯一标示符不存在,则初始化唯一标示符,并将其存入字典中,对应唯一标示符注册Cell
            if (identifier1 == nil) {
                identifier1 = [NSString stringWithFormat:@"%@", indexPath];
                [self.identifierDic setValue:identifier1 forKey:[NSString stringWithFormat:@"hotRouteCell%ld%@",self.hotDayCount, indexPath]];
                // 注册Cell
                [self.hotRouteTableView registerClass:[HotRouteTableViewCell class] forCellReuseIdentifier:identifier1];
            }
            
            HotRouteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier1 forIndexPath:indexPath];
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            if (self.hotRouteDataArray.count != 0) {
                NSMutableArray *EachRoute =[NSMutableArray arrayWithArray:[self.hotRouteDataArray[indexPath.section] EachRoute]];
                cell.EachRoute = EachRoute;
            }
            return cell;
        }
        return nil;
    }
    

    解决方案3:只要最后一个显示的cell内容不为空,将其子视图删除,成为一个空白的cell,再进行自定义。🌰:

    //解决方案3:只要最后一个显示的cell内容不为空,将其子视图删除,成为一个空白的cell,再进行自定义
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
        if (tableView == self.classicRouteTableView) {
            // 通过唯一标识符classicRouteCell创建cell实例
            ClassicRouteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:classicRouteCell forIndexPath:indexPath];
            //当cell存在且最后一个存在时 将最后的一个cell删除其子视图,成为一个空白的cell,再进行自定义cell
            //这里是使用while语句 或if语句
            if (cell) {
                while ([cell.contentView.subviews lastObject] != nil) {
                    [[cell.contentView.subviews lastObject] removeFromSuperview];
                }
            }
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            if (self.classicRouteDataArray.count != 0) {
                NSMutableArray *EachRoute =[NSMutableArray arrayWithArray:[self.classicRouteDataArray[indexPath.section] EachRoute]];
                cell.EachRoute = EachRoute;
            }
            return cell;
        }
        
        if (tableView == self.hotRouteTableView) {
            // 通过唯一标识符hotRouteCell创建cell实例
            HotRouteTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:hotRouteCell forIndexPath:indexPath];
            //当cell存在且最后一个存在时 将最后的一个cell删除其子视图,成为一个空白的cell,再进行自定义cell
            //这里是使用while语句 或if语句
            if (cell) {
                if ([cell.contentView.subviews lastObject] != nil) {
                    [[cell.contentView.subviews lastObject] removeFromSuperview];
                }
            }
    
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
            if (self.hotRouteDataArray.count != 0) {
                NSMutableArray *EachRoute =[NSMutableArray arrayWithArray:[self.hotRouteDataArray[indexPath.section] EachRoute]];
                cell.EachRoute = EachRoute;
            }
            return cell;
        }
        return nil;
    }
    

    总结

    • 当cell少或数据量不大时,可以添加标识符,或通过indexPath来创建cell的方法来解决cell的重用问题。
    • 当cell重用较多时或数据量大时,删除cell的子视图的方法来解决cell的常用问题。

    Demo_github

    参考文章

    相关文章

      网友评论

      • 古月纳兰:if (cell) {
        if ([cell.contentView.subviews lastObject] != nil) {
        [[cell.contentView.subviews lastObject] removeFromSuperview];
        }
        }

        没有什么用啊,是你cell中的这句才有用吧 for (UIView *view in self.contentView.subviews) {
        [view removeFromSuperview];
        }
        SkyMing一C:@古月纳兰 是的 前面只是代码保护一下的

      本文标题:iOS - UITableViewCell重用机制出错的解决方案

      本文链接:https://www.haomeiwen.com/subject/yvsjtxtx.html