Objective C - UITableViewCell异步加载图像

时间:2012-12-27 12:15:09

标签: objective-c uitableview

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

由于下载图像同步阻止了用户界面,我通过大中央调度实现了异步方式。

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

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

这是我的代码。

对于下载的每个图像,我将其存储在名为“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 个答案:

答案 0 :(得分: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延迟加载

答案 1 :(得分:2)

来自Prince Answer的一点修改

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];
        });
     });
  }
}

答案 2 :(得分: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 Demo

我没有深入研究AFNetworking的代码,但是当将图像加载到单元格中时,我的应用程序就像BUTTER一样运行。它看起来很棒:))

哦,这里是AFNetworking的文档:AFNetworking Documentation

答案 3 :(得分:0)

我也注意到了这种行为。

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