asynchronous加载图像到UIImage

我正在开发iOS 4应用程序与iOS 5.0 SDK和XCode 4.2。

我必须展示一些博客到UITableView。 当我检索所有的Web服务数据时,我使用这个方法来创build一个UITableViewCell:

- (BlogTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString* cellIdentifier = @"BlogCell"; BlogTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (cell == nil) { NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BlogTableViewCell" owner:nil options:nil]; for(id currentObject in topLevelObjects) { if ([currentObject isKindOfClass:[BlogTableViewCell class]]) { cell = (BlogTableViewCell *)currentObject; break; } } } BlogEntry* entry = [blogEntries objectAtIndex:indexPath.row]; cell.title.text = entry.title; cell.text.text = entry.text; cell.photo.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:entry.photo]]]; return cell; } 

但是这一行:

 cell.photo.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:entry.photo]]]; 

它是如此之慢(entry.photo有一个http url)。

有没有办法asynchronous加载该图像? 我认为这很困难,因为经常调用tableView:cellForRowAtIndexPath。

看看SDWebImage:

https://github.com/rs/SDWebImage

这是一套很棒的课程,可以为你处理所有的事情。

蒂姆

我写了一个自定义的类来做到这一点,使用块和GCD:

WebImageOperations.h

 #import <Foundation/Foundation.h> @interface WebImageOperations : NSObject { } // This takes in a string and imagedata object and returns imagedata processed on a background thread + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage; @end 

WebImageOperations.m

 #import "WebImageOperations.h" #import <QuartzCore/QuartzCore.h> @implementation WebImageOperations + (void)processImageDataWithURLString:(NSString *)urlString andBlock:(void (^)(NSData *imageData))processImage { NSURL *url = [NSURL URLWithString:urlString]; dispatch_queue_t callerQueue = dispatch_get_current_queue(); dispatch_queue_t downloadQueue = dispatch_queue_create("com.myapp.processsmagequeue", NULL); dispatch_async(downloadQueue, ^{ NSData * imageData = [NSData dataWithContentsOfURL:url]; dispatch_async(callerQueue, ^{ processImage(imageData); }); }); dispatch_release(downloadQueue); } @end 

在你的

  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // Pass along the URL to the image (or change it if you are loading there locally) [WebImageOperations processImageDataWithURLString:entry.photo andBlock:^(NSData *imageData) { if (self.view.window) { UIImage *image = [UIImage imageWithData:imageData]; cell.photo.image = image; } }]; } 

这是非常快,将加载图像影响UI或滚动速度的TableView。

***注意 – 此示例假定使用ARC。 如果没有,你将需要pipe理你自己的对象发布)

在iOS 6和更高版本中,dispatch_get_current_queue会提供弃用警告。

这是一个替代scheme,它是上面的@ElJay回答和@khanlou这篇文章的综合。

在UIImage上创build一个类别:

的UIImage + Helpers.h

 @interface UIImage (Helpers) + (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback; @end 

的UIImage + Helpers.m

 #import "UIImage+Helpers.h" @implementation UIImage (Helpers) + (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); dispatch_async(queue, ^{ NSData * imageData = [NSData dataWithContentsOfURL:url]; dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:imageData]; callback(image); }); }); } @end 

迅速:

 extension UIImage { // Loads image asynchronously class func loadFromURL(url: NSURL, callback: (UIImage)->()) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { let imageData = NSData(contentsOfURL: url) if let data = imageData { dispatch_async(dispatch_get_main_queue(), { if let image = UIImage(data: data) { callback(image) } }) } }) } } 

用法:

 // First of all remove the old image (required for images in cells) imageView.image = nil // Load image and apply to the view UIImage.loadFromURL("http://...", callback: { (image: UIImage) -> () in self.imageView.image = image }) 

考虑失败案例。

 - (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); dispatch_async(queue, ^{ NSError * error = nil; NSData * imageData = [NSData dataWithContentsOfURL:url options:0 error:&error]; if (error) callback(nil); dispatch_async(dispatch_get_main_queue(), ^{ UIImage *image = [UIImage imageWithData:imageData]; callback(image); }); }); } 

是的,这是相对容易的。 这个想法是这样的:

  1. 创build一个可变的数组来保存你的图像
  2. 如果你喜欢,用占位符填充数组(如果你不喜欢的话,也可以用NSNull对象)
  3. 创build一个方法在后台asynchronous获取图像
  4. 当一个图像到达时,交换你的占位符与真实的形象,做一个[self.tableView reloadData]

我已经testing了这种方法很多次,并给出了很好的结果。 如果你需要任何帮助/例子与asynchronous部分(我build议使用gcd的),让我知道。

如果您使用Apple为此提供的示例代码,则可以轻松完成此任务:示例代码: Lazy Image

只要看看委托rowforcell和添加icondownloader文件到你的项目。

你必须做的唯一的改变是改变对象的赞赏对象。

虽然SDWebImage和其他第三方可能是很好的解决scheme,但是如果你不喜欢使用第三方API,你也可以开发自己的解决scheme。

请参阅本教程中有关延迟加载的内容 ,这也涉及到如何在表视图中对数据进行build模。

你可能必须inheritance你的UIImageView。 我最近做了一个简单的项目来解释这个特定的任务 – 后台asynchronous图像加载 – 看看我的项目在GitHub。 具体来说,看一下KDImageView类。