2

我正在展示一张桌子。每行都有一个从 URL 加载的图像图标。

由于同步下载图像会阻塞 UI,因此我通过大型中央调度实现了异步方式。

我的问题是,当我向下和向上滚动时,由于正在重复使用单元格,因此会显示不正确的图像。

我可以猜到为什么会发生这种情况 - 这是因为重新使用的单元格更新了图像,因此,以前的单元格现在将具有新下载的错误图像。解决这个问题的理想方法是什么?

这是我的代码。

对于下载的每个图像,我将其存储在一个名为“ImageStore”的单例类中。

// set the data for each cell - reusing the cell
- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"UITableViewCell"];

   if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
                                      reuseIdentifier:@"UITableViewCell"];
    }

    // setting the image for each cell
    // first check if there is UIImage in the ImageStore already
    NSString *imageUrl = [obj objectForKey:@"image"];
    if (imageUrl) {
      if ([[ImageStore sharedStore] imageForKey:imageUrl]) {

        [[[tableView cellForRowAtIndexPath:indexPath] imageView] setImage:[[ImageStore sharedStore] imageForKey:imageUrl]];
      } else {

        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
    dispatch_async(queue, ^{
      UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:
      [NSURL URLWithString:[obj objectForKey:@"image"]]]];
      dispatch_sync(dispatch_get_main_queue(), ^{
        [[ImageStore sharedStore]setImage:image forKey:imageUrl];
        [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]withRowAnimation:UITableViewRowAnimationNone];

            [[[tableView cellForRowAtIndexPath:indexPath] imageView] setImage:image];
      });
    });
      }
    }
    return cell;
}
4

4 回答 4

4

尝试这个:

NSString *imageUrl = [obj objectForKey:@"image"];
if (imageUrl) {
  if ([[ImageStore sharedStore] imageForKey:imageUrl]) {
    [[cell imageView] setImage:[[ImageStore sharedStore] imageForKey:imageUrl]];
  } else {
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
  dispatch_async(queue, ^{
     UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:
     [NSURL URLWithString:[obj objectForKey:@"image"]]]];
  dispatch_sync(dispatch_get_main_queue(), ^{
     [[cell imageView] setImage:image];
     [cell setNeedsLayout];
  });
  });
}

编辑:检查这个grand-central-dispatch-for-ios-lazy-loading以进行 tableview 延迟加载

于 2012-12-27T12:23:21.067 回答
2

来自王子答案的一点修改

NSString *imageUrl = [obj objectForKey:@"image"];

if (imageUrl) 
{
  if ([[ImageStore sharedStore] imageForKey:imageUrl]) 
  {
      //This condition means the current cell's image has been already downloaded and stored. So set the image to imageview
      [[cell imageView] setImage:[[ImageStore sharedStore] imageForKey:imageUrl]];
  } 
  else 
  {
      //While reusing this imageView will have previous image that will be visible till the image is downloaded. So i am setting this image as nil.
      [[cell imageView] setImage:nil]; 

      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
      dispatch_async(queue, ^{

         //Called Immediately.
         UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:
         [NSURL URLWithString:[obj objectForKey:@"image"]]]];
         dispatch_sync(dispatch_get_main_queue(), ^{

           //Called when the image is downloaded
           //Store in any external object. So that next time reuse this will not be downloaded
           [[ImageStore sharedStore]setImage:image forKey:imageUrl];
           //Also set the image to the cell
           [[cell imageView] setImage:image];
           [cell setNeedsLayout];
        });
     });
  }
}
于 2012-12-27T12:50:13.137 回答
2

看看AFNetworking......他们使处理网络回调变得容易!我推荐AFNetworking过去,ASIHTTPRequest因为AFNetworking它会保持更新,ASIHTTPRequest而不是......他们只是停止了开发。

这是一个如何使用AFNetworking异步下载图像的示例:

NSDictionary * object = [self.array objectAtIndex:indexPath.row];
NSDictionary * image = [object valueForKey:@"image"];
NSString *imageUrl = [image valueForKeyPath:@"url"];
NSURL *url = [NSURL URLWithString:imageUrl];
[cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"empty-profile-150x150.png"]];

setImageWithURL: placeholderImage:方法是我曾经这样做的......而不是多个方法和代码行,我用这一行完成了所有事情。

这整行代码完全符合您的要求。真的没有必要重新创建轮子:)。尽管通过较低级别的编程来真正了解幕后真正发生的事情的实现确实有所帮助。

查看链接以下载该库并查看有关如何使用它的更多示例……说真的,它使您的生活更轻松,而不必担心线程和 GCD。

AFNetworking 演示

我还没有深入研究代码,AFNetworking但是在将图像加载到单元格时,我的应用程序运行起来像黄油一样。看起来不错 :)

哦,这里的文档AFNetworkingAFNetworking Documentation

于 2013-08-24T21:30:24.197 回答
0

我也注意到了这种行为。

我能找到的唯一通用解决方案是禁用单元重用。

于 2012-12-27T12:32:34.403 回答