UITableView con imágenes se desplaza muy lentamente

Posible duplicado:
Vista de tabla con imágenes, carga lenta y desplazamiento

Tengo un UITableView que descarga imágenes para UITableViewCells desde un servidor. Observé que el tableView se desplaza muy lentamente.

Pensé que esto podría ser un problema de descarga, pero me he dado cuenta de que la tabla todavía se desplaza lentamente después de que la descarga ha terminado y el tamaño del icono de la imagen es menor.

Busqué en Google pero no encontré ninguna ayuda.

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { btnBack.hidden = FALSE; static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.backgroundColor = [UIColor clearColor]; cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0]; cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0]; cell.textLabel.textColor = [UIColor blackColor]; cell.textLabel.highlightedTextColor = [UIColor blackColor]; } cell.textLabel.text = [NSString stringWithFormat:@" %@", [test.arrTitle objectAtIndex:indexPath.row]]; NSString *Path; Path = [NSString stringWithFormat:@"http://%@",[test.arrImages objectAtIndex:indexPath.row]]; NSLog(@"image-->%@",[test.arrImages objectAtIndex:indexPath.row]); NSString *strImage = Path; NSURL *url4Image = [NSURL URLWithString:strImage]; NSData *data = [NSData dataWithContentsOfURL:url4Image]; image =[[UIImage alloc] initWithData:data]; cell.imageView.image =image; [image release]; return cell; } 

Si bien mi respuesta original, a continuación, intentó resolver varios problemas clave asociados con la recuperación asíncrona de imágenes, sigue siendo fundamentalmente limitada. Una implementación adecuada también aseguraría que si se desplazaba rápidamente, las celdas visibles se priorizaran sobre las celdas que se desplazaron fuera de la pantalla. También respaldaría la cancelación de solicitudes anteriores (y las cancelaría para usted cuando corresponda).

Si bien podríamos agregar este tipo de capacidades al siguiente código, probablemente sea mejor adoptar una solución establecida y comprobada que utilice las tecnologías NSOperationQueue y NSCache analizan a continuación, pero que también aborde los problemas anteriores. La solución más fácil es adoptar una de las categorías UIImageView establecidas que admite la recuperación de imágenes asíncrona. Las bibliotecas AFNetworking y SDWebImage tienen categorías de UIImageView que manejan con gracia todos estos problemas.


Puede usar NSOperationQueue o GCD para realizar su carga diferida (consulte la Guía de progtwigción de simultaneidad para consultar las diferentes tecnologías de operaciones asíncronas). El primero tiene la ventaja de que puede especificar con precisión cuántas operaciones concurrentes son permisibles, lo cual es muy importante al cargar imágenes desde la web porque muchos servidores web limitan la cantidad de solicitudes simultáneas que aceptarán de un cliente determinado.

La idea básica es:

  1. Enviar solicitud de los datos de imagen en una cola de fondo separada;
  2. Cuando termine de descargar la imagen, envíe la actualización de la IU a la cola principal porque nunca debería hacer actualizaciones de IU en el fondo;
  3. Al ejecutar el código de actualización de UI final distribuido en la cola principal, asegúrese de que UITableViewCell aún esté visible y de que no se haya eliminado y reutilizado porque la celda en cuestión se desplazó fuera de la pantalla. Si no haces eso, la imagen incorrecta puede aparecer momentáneamente.

Debería reemplazar su código por algo como el siguiente código:

Primero, defina una propiedad para su NSOperationQueue que usará para descargar imágenes, así como una NSCache para almacenar esas imágenes:

 @property (nonatomic, strong) NSOperationQueue *imageDownloadingQueue; @property (nonatomic, strong) NSCache *imageCache; 

Segundo, inicialice esta cola y el caché en viewDidLoad :

 - (void)viewDidLoad { [super viewDidLoad]; self.imageDownloadingQueue = [[NSOperationQueue alloc] init]; self.imageDownloadingQueue.maxConcurrentOperationCount = 4; // many servers limit how many concurrent requests they'll accept from a device, so make sure to set this accordingly self.imageCache = [[NSCache alloc] init]; // the rest of your viewDidLoad } 

En tercer lugar, su cellForRowAtIndexPath podría verse así:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { btnBack.hidden = FALSE; static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; cell.selectionStyle = UITableViewCellSelectionStyleNone; cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; cell.backgroundColor = [UIColor clearColor]; cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0]; cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0]; cell.textLabel.textColor = [UIColor blackColor]; cell.textLabel.highlightedTextColor = [UIColor blackColor]; } cell.textLabel.text = [NSString stringWithFormat:@" %@", [test.arrTitle objectAtIndex:indexPath.row]]; // code change starts here ... initialize image and then do image loading in background NSString *imageUrlString = [NSString stringWithFormat:@"http://%@", [test.arrImages objectAtIndex:indexPath.row]]; UIImage *cachedImage = [self.imageCache objectForKey:imageUrlString]; if (cachedImage) { cell.imageView.image = cachedImage; } else { // you'll want to initialize the image with some blank image as a placeholder cell.imageView.image = [UIImage imageNamed:@"blankthumbnail.png"]; // now download in the image in the background [self.imageDownloadingQueue addOperationWithBlock:^{ NSURL *imageUrl = [NSURL URLWithString:imageUrlString]; NSData *imageData = [NSData dataWithContentsOfURL:imageUrl]; UIImage *image = nil; if (imageData) image = [UIImage imageWithData:imageData]; if (image) { // add the image to your cache [self.imageCache setObject:image forKey:imageUrlString]; // finally, update the user interface in the main queue [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // Make sure the cell is still visible // Note, by using the same `indexPath`, this makes a fundamental // assumption that you did not insert any rows in the intervening // time. If this is not a valid assumption, make sure you go back // to your model to identify the correct `indexPath`/`updateCell` UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath]; if (updateCell) updateCell.imageView.image = image; }]; } }]; } return cell; } 

En cuarto lugar, y finalmente, aunque uno podría inclinarse a escribir código para purgar la memoria caché en situaciones de memoria baja, resulta que lo hace de forma automática, por lo que no es necesario un manejo adicional. Si simula manualmente una situación con poca memoria en el simulador, no verá desalojar sus objetos porque NSCache no responde UIApplicationDidReceiveMemoryWarningNotification , pero durante la operación real, cuando la memoria es baja, se borrará la memoria caché. En realidad, NSCache ya no responde con elegancia a las situaciones de memoria baja, por lo que debería agregar observador para esta notificación y vaciar el caché en situaciones de poca memoria.

Podría sugerir un montón de otras optimizaciones (por ejemplo, tal vez también el almacenamiento en caché de imágenes en el almacenamiento persistente para simplificar las operaciones futuras; de hecho, puse toda esta lógica en mi propia clase AsyncImage ), pero primero veré si esto resuelve el problema de rendimiento básico.

Escriba esto en su UITableView cellForRowAtIndex: Method

 asyncImageView = [[AsyncImageView alloc]initWithFrame:CGRectMake(30,32,100, 100)]; [asyncImageView loadImageFromURL:[NSURL URLWithString:your url]]; [cell addSubview:asyncImageView]; [asyncImageView release]; 

Necesita importar la AsyncImageView class y crear un objeto para esa clase

Como se dijo anteriormente: no levante objetos pesados ​​en cellForRowAtIndexPath. Puede desplazarse fácilmente utilizando GCD. cargar imágenes de un hilo de fondo usando bloques

Debería utilizar un NSOperationQueue para manejar la carga diferida de imágenes y una tabla vista personalizada. Puede obtener un ejemplo de muestra para NSOperationQueue aquí

Google for tweetie tableviewcell personalizado Eso debería establecerlo en la dirección correcta.

Apple tiene un proyecto de muestra para descargar imágenes en tableViews: LazyTableImages

Es aconsejable que almacene las imágenes en una matriz y las viewDidLoad en su viewDidLoad , y luego en su cellForRowAtIndexPath: simplemente configure

 cell.imageView.image = [yourImageArray objectAtIndex:indexPath.row]; 

En lo que respecta a la lentitud, es porque bloquea el hilo principal con la descarga de URLDATA en su método cellForRowAtIndexPath , por lo que mientras se desplaza a menos y hasta que la imagen no sea cellForRowAtIndexPath , se bloqueará el hilo principal en el que se ejecuta la aplicación.

Desplazamiento muy lento porque está cargando imágenes en el hilo principal, es decir, sincrónicamente. Puede hacer lo mismo en el hilo de fondo, es decir, sin sincronizar, eche un vistazo a SDWebImage .