使用GCD加载asynchronousUITableViewCell图像

我目前正试图加载一个UITableView列表的Flickr照片(cs193p iOS斯坦福,分配5)。 为了避免UI阻塞事件,我推迟了每个单元格的缩略图下载到一个不同的队列中(但是将UI更新回主队列中)。 这段代码并不是asynchronous加载图像,但是一旦点击了UITableViewCell行就会添加一个缩略图。 (见下面的截图)。 任何想法我做错了什么?

PS:我已经看了一些其他stackoverflow问题和苹果公司的LazyTableImages的例子,但我仍然相信,这是达到预期的结果最干净的方式。

谢谢!

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Photo List Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; // Configure the cell NSDictionary *photo = [self.photoList objectAtIndex:indexPath.row]; if (photo != nil) { if ([[photo objectForKey:@"title"] length] > 0) { cell.textLabel.text = [photo objectForKey:@"title"]; } else if ([[[photo objectForKey:@"description"] objectForKey:@"_content"] length] > 0) { cell.textLabel.text = [[photo objectForKey:@"description"] objectForKey:@"_content"]; } else { cell.textLabel.text = @"Unknown"; } } cell.imageView.image = [[UIImage alloc] initWithCIImage:nil]; // Fetch using GCD dispatch_queue_t downloadThumbnailQueue = dispatch_queue_create("Get Photo Thumbnail", NULL); dispatch_async(downloadThumbnailQueue, ^{ UIImage *image = [self getTopPlacePhotoThumbnail:photo]; dispatch_async(dispatch_get_main_queue(), ^{ if ([self.tableView.visibleCells containsObject:cell]) { [cell.imageView setImage:image]; } }); }); dispatch_release(downloadThumbnailQueue); return cell; } 

在点击一行之前 在点击一行之前

select行后 选择行后

更新:对于那些有兴趣的,这是我使用的最终代码:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Photo List Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; // Configure the cell NSDictionary *photo = [self.photoList objectAtIndex:indexPath.row]; if (photo != nil) { if ([[photo objectForKey:@"title"] length] > 0) { cell.textLabel.text = [photo objectForKey:@"title"]; } else if ([[[photo objectForKey:@"description"] objectForKey:@"_content"] length] > 0) { cell.textLabel.text = [[photo objectForKey:@"description"] objectForKey:@"_content"]; } else { cell.textLabel.text = @"Unknown"; } } cell.imageView.image = [[UIImage alloc] initWithCIImage:nil]; // Fetch using GCD dispatch_queue_t downloadThumbnailQueue = dispatch_queue_create("Get Photo Thumbnail", NULL); dispatch_async(downloadThumbnailQueue, ^{ UIImage *image = [self getTopPlacePhotoThumbnail:photo]; dispatch_async(dispatch_get_main_queue(), ^{ UITableViewCell *cellToUpdate = [self.tableView cellForRowAtIndexPath:indexPath]; // create a copy of the cell to avoid keeping a strong pointer to "cell" since that one may have been reused by the time the block is ready to update it. if (cellToUpdate != nil) { [cellToUpdate.imageView setImage:image]; [cellToUpdate setNeedsLayout]; } }); }); dispatch_release(downloadThumbnailQueue); return cell; } 

设置缩略图后,您应该在单元setNeedsLayout上调用setNeedsLayout

从苹果文件:

“当你想调整视图子视图的布局时,在应用程序的主线程上调用这个方法,这个方法会logging下请求并立即返回,因为这个方法不会强制立即更新,而是等待下一次更新循环中,您可以使用它来使多个视图的布局无效,这些行为允许您将所有布局更新合并为一个更新周期,这通常更适合于性能。

另一个解决scheme(以及我用于该分配的解决scheme)是在将UIImage分配给该imageView后,在该单元的imageView上调用setNeedsDisplay。

所以基本上,我创build了一个名为FlickrDownloader的类,它包装了FlickrFetcher(以及我的caching代码)的function。 FlickrDownloader有这个方法:

 (void)getImageForPhotoInfo:(NSDictionary *)photoInfo ofFormat:(FlickrPhotoFormat)format withCallback:(image_setter_callback_t)callback; 

这基本上是对FlickrFetcher的相同function的调用,但它添加了一个callback,这是在完成下载照片后调用的。 在更新UITableViewCell的imageView的情况下,callback看起来像这样:

 image_setter_callback_t setImage = ^(UIImage *image){ cell.imageView.image = image; [cell.imageView setNeedsDisplay]; };