使用cellForRowAtIndexPath中的dispatch_async加载图像

时间:2016-05-14 05:34:35

标签: ios objective-c uitableview nsdata dispatch-async

我正试图从网址加载图片。

以下是我的代码..

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

  UITableViewCell *cell = nil;

    cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@""];

    if (cell == nil)
    {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:nil];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    }

    dispatch_async (dispatch_get_main_queue(), ^{

        NSData * storeImageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:productImageArray[indexPath.row]]];

        self.productImage.image = [UIImage imageWithData:storeImageData];
        });

            [cell.contentView addSubview:self.productImage];

return cell;
}

问题是,

  1. UI冻结,直到图像加载完毕。
  2. 只有最后一个单元格正在加载图像,但其余单元格未加载图像。
  3. 我该如何解决这个问题?

6 个答案:

答案 0 :(得分:3)

我觉得这是异步下载图像的最佳方法。我将始终使用以下方法完成此任务。

cellForRowAtIndexPath:

 NSURL *imgURL = [[NSURL URLWithString:productImageArray[indexPath.row]];
 [self downloadImageWithURL:imgURL
            completionBlock:^(BOOL succeeded, UIImage *image) {
                          self.productImage.image=image;
     }];

并添加此方法

- (void)downloadImageWithURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
{
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
     [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue]
          completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
             if ( !error )
              {
                data = [NSData dataWithContentsOfURL:url];
                UIImage *image = [[UIImage alloc] initWithData:data];
                completionBlock(YES,image);
              } else{
                 NSLog(@"Error in downloading image:%@",url);
                 completionBlock(NO,nil);
              }
           }];
}

使用此功能,即使我们在下载图像时出现任何问题也能更好地了解。

答案 1 :(得分:2)

@MGR,你可以使用Dispatch async,但是有一个库https://www.cocoacontrols.com/controls/hanekeswift,一个很棒的库。它将处理下载图像,它有一个方法来加载imageview.public中的图像  func hnk_setImageFromURL(URL: NSURL, placeholder: UIImage? = default, format: Haneke.Format<UIImage>? = default, failure fail: ((NSError?) -> ())? = default, success succeed: ((UIImage) -> ())? = default)

它还具有缓存功能。所以你可以在cellForRow中简单地实现这个功能。多数民众赞成它将由Haneke处理。

*请使用此框架* https://github.com/onevcat/Kingfisher

这比Hanekeswift

带来了更好的性能和自定义

答案 2 :(得分:1)

尝试NSURLSessionTask

NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    if (data) {
        UIImage *image = [UIImage imageWithData:data];
        if (image) {
            dispatch_async(dispatch_get_main_queue(), ^{
                UITableViewCell * cell = (id)[tableView cellForRowAtIndexPath:indexPath];
                if (cell)
                    cell. productImage.image = image;
            });
        }
    }
}];
[task resume];

答案 3 :(得分:1)

您正在主UI线程上下载同步图像,导致屏幕冻结。

请按照以下文章解决此问题:

iOS: How To Download Images Asynchronously (And Make Your UITableView Scroll Fast)

也回答你的第二点:

您似乎正在使用在cellForRowAtIndexPath方法之外实例化的单个imageView,然后您尝试将其添加到每个单元格中,这将导致图像从之前的单元格中删除,并添加到当前单元格中

因为视图只能有一个超级视图,一旦您尝试在其他视图中添加子视图,它将从当前的超级视图中删除

答案 4 :(得分:1)

您可以使用GCD在后台线程中加载图像,如下所示:

  //get a dispatch queue

    dispatch_queue_t concurrentQueue =  dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //this will start the image loading in bg

    dispatch_async(concurrentQueue, ^{        
        NSData *image = [[NSData alloc] initWithContentsOfURL:imageURL];

        //this will set the image when loading is finished

        dispatch_async(dispatch_get_main_queue(), ^{
            imageView.image = [UIImage imageWithData:image];
        });
    });

但解决这些问题的最简单方法是使用UIImageView类别,例如SDWebImageAFNetworking提供的类别。如果你愿意,你可以编写自己的代码来处理上述问题,但这是很多工作,上面的UIImageView类别已经为你完成了这个。

答案 5 :(得分:1)

试试这个:

    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
    cell.imageView.image = image;  

或者有时我所做的是更新模型,然后在特定的indexPath处重新加载单元格:

  myModel.image = downloadedThumbImage;
  [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                                  withRowAnimation:UITableViewRowAnimationNone];  


  - (UITableViewCell *)tableView:(UITableView *)tableView    cellForRowAtIndexPath:(NSIndexPath *)indexPath
  {
    static NSString *CellIdentifier = @"top50places";
    UITableViewCell *cell = [tableView   dequeueReusableCellWithIdentifier:CellIdentifier];

   //getting the selected row image 
  NSDictionary* currentImageDictionary=[self.topfifty objectAtIndex:indexPath.row];//topFifty is an array of image dictionaries

   UIImage *currentImage = [currentImageDictionary objectForKey:@"image"];

  if (currentImage) { 
      // we already fetched the image, so we just set it to the cell's image view
     cell.imageView.image = currentImage;
  }
  else {
       // we don't have the image, so we need to fetch it from the server  

   // In the meantime, we can set some place holder image
   UIImage *palceholderImage = [UIImage imageNamed:@"placeholder.png"];
   cell.imageView.image = palceholderImage;

  // set the placeholder as the current image to your model, so you won't 
  // download the image multiple times (can happen if you reload this cell while 
  // download is in progress)
  [currentImageDictionary setObject:palceholderImage forKey:@"image"];

  // then download the image
  // creating the download queue 
  dispatch_queue_t downloadQueue=dispatch_queue_create("thumbnailImage", NULL);

  dispatch_async(downloadQueue, ^{
     UIImage *downloadedThumbImage=[self getImage:currentImageDictionary] ;

     //Need to go back to the main thread since this is UI related
     dispatch_async(dispatch_get_main_queue(), ^{
            // store the downloaded image in your model
            [currentImageDictionary setObject:image forKey:@"image"];

            // update UI
            UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
            cell.imageView.image = image;    
     });
  });
  dispatch_release(downloadQueue);

   }

 return cell;
 }