UITableViewCell 在出队时重绘

Posted

技术标签:

【中文标题】UITableViewCell 在出队时重绘【英文标题】:UITableViewCell is redrawing on dequeue 【发布时间】:2015-05-06 04:10:29 【问题描述】:

我的设置

我有一个UITableViewCell,它位于我的主情节提要的UITableViewController 中。它填充了一些从 REST API 中提取的 JSON 数据,这将导致每个单元格的高度可变。 UIImageViewsUILabels 有各种不同的高度和风格,想想 Instagram 风格。

我的问题

当我滚动到第 5 或第 6 个单元格,然后返回时,它们开始重绘和重叠,因此文本会混合,线条会重绘,等等。

我的尝试

这似乎是 SO 上的一个常见问题,所以我尝试了几个已发布的解决方案。看来我的问题可能与其他人面临的问题相同,也就是说,每次出队时我都会在我的单元格上调用addSubview,但我尝试检查单元格是否已经存在。我在某个地方遇到了另一个帖子(对不起,我不记得在哪里),这表明因为我在情节提要中创建它,它已经初始化并且if ( !cell ) 已经返回 false,所以我不知道如何防止它重绘。

当我尝试从情节提要中删除单元格并以编程方式创建它时,我收到一条错误消息,提示它找不到具有我的标识符 @"Cell" 的单元格。

我也尝试过有人在出队时删除所有子视图的解决方案,所以我使用了:

for ( UIView *view in cell.contentView.subviews ) 
     if ([view isKindOfClass:[UIView class]]) 
         [view removeFromSuperview];
     

它什么也没找到。

【问题讨论】:

最简单的解决方案是在情节提要中创建所有子视图,这样您就不必在代码中添加任何内容。如果要在代码中添加它们,请创建一个 UITableViewCell 子类,并在其 init 方法中添加子视图。在 cellForRowAtIndexPath 中添加子视图很少是最好的方法。 谢谢@rdelmar。当我不确定其中会包含哪些内容时,我该怎么做?我知道我会有一张图片和至少 2 个标签,但我可以有额外的标签或按钮。这就是你建议子类化它的原因吗? 如果您基于 indexPath 添加这些额外的视图(有些行有它们,有些不基于数据源),那么您确实需要在 cellForRow 中添加它们。如果您没有太多不同的单元格类型,另一种解决方案是在情节提要中制作多个单元格,然后根据数据源将相应的单元格出列。 谢谢,我会在早上试一试,然后回复您。 @rdelmar - 我睡不着,不得不试一试,最后让它工作了。我确信它可以改进,但 MVP 是我现在需要的。您可以将此作为答案发布,以便我检查一下吗? 【参考方案1】:

@rdelmar 的评论是正确的。你不应该做你正在做的事。可能有效,但这是一种糟糕的形式,你不想养成坏习惯。

首先,利用面向对象的编程。单元格应该能够根据您要求它显示的数据进行自我配置。表格视图不应该设计单元格。

UITableViewCells 需要针对速度进行优化。创建和添加子视图是一个缓慢的过程。做一次没问题,但是单元格会被重用(系统优化),你应该重用第一次创建单元格时添加的现有视图。

例如,您可以隐藏不需要的子视图。您可能希望在 -prepareForReuse 中执行此操作。您可以在 -layoutSubviews 中移动它们。或者在-updateConstraints中改变子视图的位置。

通常您只想将要显示的数据从数据源(通常是视图控制器)传递给表格视图单元子类。让单元格完成显示工作。

【讨论】:

谢谢,@dave-batton。你在这两件事上都是正确的,1)rdelmar 是正确的,2)我需要确保我没有学习坏习惯。我确信在解决这个问题时我会养成很多坏习惯,但我会尽我所能去经历和重构并学习最佳实践。感谢您的帮助。【参考方案2】:

当你在你的单元格出列后添加你的子视图时,给你的子视图一个标签。这样,当你将一个单元格出列时,你可以先用你的标签检查是否存在子视图,如果存在,在添加新视图之前将其删除:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath

    ...
    // try to dequeue a cell
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:<yourCellIdentifier>];

    if( !cell )
    
        // create a new cell if necessary
    

    static int _myViewTag = 1000987 // give it a high int : low value are used by the system in cells

    UIView *v = cell.contentView viewWithTag:_myViewTag];

    if( v ) // subview with such tag already exists, so remove it.
        [v removeFromSuperview];

    // now add your new subview
    [cell.contentView addSubview:<yourView>];

    // adjust height of cell to your view.
    ...

【讨论】:

【参考方案3】:

尝试在您的cell 类 中添加一个新方法将cell 重置为其默认样式,并在dequeueCell 之后调用此方法。

【讨论】:

谢谢@britto-thomas,您能详细说明一下吗? 只需将单元格中所有子视图的框架设置为方法中的默认样式即可。或者,如果您的单元格有不同的样式,请在自定义单元格类中添加不同的单元格设计方法,并在加载前根据需要调用它。 我遇到的一个问题是,当我尝试遍历子视图时,似乎什么都没有。有关我尝试运行的代码,请参阅我原始帖子的最后一部分。【参考方案4】:

管理此问题的最有效方法是将UITableViewCell 子类化并将所有需要的Views 添加为属性。所以现在当一个单元格出现“回收”时,你知道在哪里可以找到旧视图,例如:

[cell.myTextLabel setText:@""];

aaa,你已经完成了。

UPDATE 如果您只有少量“类型”单元格,则创建子类是有意义的。为每个创建一个子类。它变得多么复杂取决于您的具体情况。但我已经做到了,发现它是最有效的方法。

UPDATE 2 或者您可以在情节提要中制作多个单元格,然后根据数据源将相应的单元格出列,保存所有编码。

【讨论】:

以上是关于UITableViewCell 在出队时重绘的主要内容,如果未能解决你的问题,请参考以下文章

JS优先队列排序。出队时,先找出优先级最高的元素,再按照先进先出出队。

UITableViewCell AFNetworking 仅加载一次带有动画的图像

出队时在 BFS 上将节点标记为已访问

线程:PyQt 因“出队时队列中的未知请求”而崩溃

SPFA 可不可以优化。。

UITableViewCell 正确地从 UITablView 出列,但它没有显示传递的值