Cargando una image en UIImage de forma asíncrona

Estoy desarrollando una aplicación iOS 4 con iOS 5.0 SDK y XCode 4.2.

Tengo que mostrar algunos blogs en una UITableView. Cuando he recuperado todos los datos del service web, utilizo este método para crear una 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; } 

Pero esta línea:

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

es tan lento (entry.photo tiene una url http).

¿Hay alguna forma de cargar esa image de forma asíncrona? Creo que es difícil porque tableView: cellForRowAtIndexPath se llama con mucha frecuencia.

Eche un vistazo a SDWebImage:

https://github.com/rs/SDWebImage

Es un set fantástico de classs que se encarga de todo para ti.

Tim

Escribí una class personalizada para hacer exactamente esto, usando bloques y 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 

Y en tu

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

Es muy rápido y cargará las imágenes afectando la interfaz de usuario o la velocidad de desplazamiento de TableView.

*** Nota: este ejemplo supone que se está utilizando ARC. De lo contrario, necesitará administrar sus propias publicaciones en objects)

En iOS 6 y posteriores, dispatch_get_current_queue proporciona advertencias de desaprobación.

Aquí hay una alternativa que es una síntesis de la respuesta de @ElJay arriba y el artículo de @khanlou aquí .

Crear una categoría en UIImage:

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 

Rápido:

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

Uso:

 // First of all remove the old image (requinetworking 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 }) 

Considerando caso de falla.

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

Sí, es relativamente fácil. La idea es algo como:

  1. Crea una matriz mutable para save tus imágenes.
  2. Rellene la matriz con marcadores de position si lo desea (o NSNull objects si no lo hace)
  3. Cree un método para search sus imágenes de forma asíncrona en segundo plano.
  4. Cuando haya llegado una image, cambie el marcador de position con la image real y realice una [self.tableView reloadData]

He probado este enfoque muchas veces y da excelentes resultados. Si necesita ayuda / ejemplos con la parte asincrónica (recomiendo usar gcd para eso) avíseme.

fácilmente puede hacerlo perfectamente si usa el código de ejemplo provisto por Apple para este propósito: el código de ejemplo: Lazy Image

Solo mire el delegado rowforcell y agregue los files icondownloader a su proyecto.

El único cambio que debes hacer es cambiar el object apprecord con tu object.

Si bien SDWebImage y otras soluciones de terceros pueden ser excelentes, si no estás interesado en utilizar API de terceros, también puedes desarrollar tu propia solución.

Consulte este tutorial sobre la carga perezosa que también habla sobre cómo debe modelar sus datos dentro de la vista de tabla.

Probablemente tenga que subclasificar su UIImageView. Recientemente hice un proyecto simple para explicar esta tarea en particular: carga de image asíncrona de background, echa un vistazo a mi proyecto en GitHub. Específicamente, mira la class KDImageView .