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。
我写了一个自定义的类来做到这一点,使用块和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); }); }); }
是的,这是相对容易的。 这个想法是这样的:
- 创build一个可变的数组来保存你的图像
- 如果你喜欢,用占位符填充数组(如果你不喜欢的话,也可以用NSNull对象)
- 创build一个方法在后台asynchronous获取图像
- 当一个图像到达时,交换你的占位符与真实的形象,做一个
[self.tableView reloadData]
我已经testing了这种方法很多次,并给出了很好的结果。 如果你需要任何帮助/例子与asynchronous部分(我build议使用gcd的),让我知道。
如果您使用Apple为此提供的示例代码,则可以轻松完成此任务:示例代码: Lazy Image
只要看看委托rowforcell和添加icondownloader文件到你的项目。
你必须做的唯一的改变是改变对象的赞赏对象。
虽然SDWebImage和其他第三方可能是很好的解决scheme,但是如果你不喜欢使用第三方API,你也可以开发自己的解决scheme。
请参阅本教程中有关延迟加载的内容 ,这也涉及到如何在表视图中对数据进行build模。
你可能必须inheritance你的UIImageView。 我最近做了一个简单的项目来解释这个特定的任务 – 后台asynchronous图像加载 – 看看我的项目在GitHub。 具体来说,看一下KDImageView类。
- iPhone – Grand Central调度主线程
- asynchronous过程里面的JavaScript for循环
- C#中Task.FromResult <TResult>的用法是什么
- 使用SqlCommandasynchronous方法时性能糟糕
- 所有的JavaScriptcallback是asynchronous的吗? 如果没有,我怎么知道哪些是?
- Java中的asynchronousIO?
- 如何阐明asynchronous和并行编程之间的区别?
- 如何检查Android的互联网接入? InetAddress永远不会超时
- entity frameworkSaveChanges()与SaveChangesAsync()和Find()与FindAsync()