【问题标题】:Custom UITableViewCell with large UIImageView causing scroll lag具有大 UIImageView 的自定义 UITableViewCell 导致滚动滞后
【发布时间】:2012-05-11 03:04:34
【问题描述】:

我有一个由 UIImageView 和 UILabel 组成的自定义 UITableViewCell。单元格为 320x104px,imageView 占据了整个区域,标签位于前面。只有 8 个单元格。

在 ViewDidLoad 中,我预先创建了所有需要的图像,并将它们以正确的尺寸缓存在字典中。

当我滚动 UITableView 时,每次遇到新单元格时都会出现明显的延迟。这对我来说毫无意义,因为它使用的图像已经创建和缓存。我对单元格的要求只是让它的 UIImageView 呈现图像。

我正在使用一个自定义单元格,其视图位于 xib 中,并配置我的 UITableView 以将其用于:

[self.tableView registerNib:[UINib nibWithNibName:@"ActsCell" bundle:nil] forCellReuseIdentifier:myIdentifier];

单元创建和配置:

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{  
    NSString* reuseIdentifier = @"ActsCell";
    ActsCell* cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
    // Configure the cell...
    [self configureCell:cell atIndexPath:indexPath];
    return cell;
}

- (void)configureCell:(ActsCell *)cell atIndexPath:(NSIndexPath *)indexPath 
{
    Act* act = [self.acts objectAtIndex:indexPath.row];
    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.title.text = act.name;
    cell.imageView.image = [self.imageCache objectForKey:act.uid];
}

什么可能导致滞后?尝试做任何异步操作似乎没有任何好处,因为所有耗时的工作都已完成。

【问题讨论】:

  • 我能想到两件事。一是出队失败是因为您没有正确设置reusableIdentifier。另一个是图像的大小。即使加载了图像,CG 也需要在显示之前对图像数据进行处理,这可能涉及到缩放、合成等成本高昂的操作。
  • @Lvsti 你能详细说明我没有正确设置reuseIdentifier吗?
  • 你说得对,可能不是这样,否则出队时你不会得到任何单元格......
  • 如果你的意思是我不使用 if(cell == nil){ //Create cell },如果使用 registerNib:forCellReuseIdentifier: 则不需要这样做,因为单元格创建被推迟到 VC。
  • 是的,我意识到了这一点。至于图片,它们的尺寸是多少?

标签: performance uitableview uiimageview uiimage


【解决方案1】:

您是否会从本地文件中加载图像?

通过使用 Instruments,我发现 UIImage 中有一些延迟加载机制 - 真实图像数据仅在主线程上渲染时才从 PNG 解压缩,这导致滚动时出现延迟。

因此,在使用-initWithContentsOfFile: 方法加载UIImage 后,我添加了代码以将此图像的内容呈现到屏幕外上下文,将该上下文保存为新的UIImage 并将其用于UIImageView UITableViewCell,这让卷轴再次变得流畅悦目。

作为参考,我使用简单的代码来强制在单独的线程中读取图像内容(使用 ARC):

UIImage *productImage = [[UIImage alloc] initWithContentsOfFile:path];

CGSize imageSize = productImage.size;
UIGraphicsBeginImageContext(imageSize);
[productImage drawInRect:CGRectMake(0, 0, imageSize.width, imageSize.height)];
productImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

而且我认为以这种方式创建的UIImage 的表面将采用适合渲染的受支持格式,这也将减轻在主线程上渲染它所需的工作。

编辑:UIGraphicsGetImageFromCurrentImageContext() 的文档说它应该只在主线程中使用,但在网络或 SO 上搜索显示从 iOS 4 开始,UIGraphics.. 方法变得线程安全。

【讨论】:

  • 这对我来说绝对是无价之宝。我一直遭受在 UICollectionView 上的不稳定滚动,每个单元格中都有 UIImageViews 来显示照片。我尝试了 很多 不同的解决方案,但我接近 60FPS 的唯一方法是非常 hacky。我尝试的大多数解决方案都涉及使用 imageWithContentsOfFile 预加载 UIImages 的 NSCache,但无论我做什么,随着新单元格出现在屏幕上,在我实现上面的代码之前,总会有一点跳跃。正如您所说,必须在 imageWithContentsOfFile 中内置某种延迟加载机制,而文档中没有提到。
  • 这对我有用,在存储 UIImage 而不是下载的数据然后在每次使用时转换为 UIImage 之后会更加流畅。非常感谢。
  • 一直在寻找缓慢/不稳定的原因。先生,你是英雄
  • 很抱歉提出旧线程,但我有类似的问题,但我无法弄清楚如何/在哪里使用上面的代码 cellForRowAtIndexPath
  • 以上代码不适用于cellForRowAtIndexPath。任何平滑滚动具有大量数据(在这种情况下为图像)的单元格的场景都是关于将加载的最重部分移动到单独的线程。这段代码是用于并行工作线程的,它将确保图像是在工作线程中加载的,而不是在主 UI 线程中。 ---- 但是您应该首先学习如何在单独的线程中为表格视图单元格加载图像,这是不同的主题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-17
  • 1970-01-01
  • 1970-01-01
  • 2015-02-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多