Chargement de l'image UITableViewCell asynchronous à l'aide de GCD

J'essaie actuellement de charger une list UITableView de Flickr Photo (cs193p iOS Stanford, affectation 5). Pour éviter l'événement de blocage de l'interface user, j'ai reporté le téléchargement de vignettes de chaque cellule dans une autre queue (mais je mets à jour l'interface user dans la queue principale). Ce code ne charge pas les images de manière asynchronous, mais ajoute une vignette une fois que je clique sur la ligne UITableViewCell. (voir les captures d'écran ci-dessous). Une idée de ce que je fais mal?

PS: J'ai déjà regardé quelques autres questions de stackoverflow et l'exemple LazyTableImages d'Apple, mais je rest convaincu que c'est le moyen le plus propre d'get le résultat souhaité.

Merci!

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSSsortingng *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; } 

Avant de cliquer sur une ligne Avant de cliquer sur une ligne

Après avoir sélectionné la ligne Après avoir sélectionné la ligne

MISE À JOUR: Pour ceux qui sont intéressés, voici le dernier code que j'ai utilisé:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSSsortingng *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; } 

Vous devez appeler setNeedsLayout sur la cellule après avoir défini la vignette.

Du Apple Doc:

"Appelez cette méthode sur le thread principal de votre application lorsque vous souhaitez ajuster la disposition des sous-vues d'une vue.Cette méthode prend note de la requête et renvoie immédiatement.Cette méthode ne force pas une mise à jour immédiate, mais attend la prochaine mise à jour cycle, vous pouvez l'utiliser pour invalider la layout de plusieurs vues avant la mise à jour de ces vues.Ce comportement vous permet de consolider toutes vos mises à jour en un seul cycle de mise à jour, ce qui est généralement meilleur pour les performances.

Une autre solution (et celle que j'ai utilisée pour cette tâche) consiste à appeler setNeedsDisplay sur l'imageView de la cellule une fois que vous avez assigné la UIImage à cette imageView.

Donc, fondamentalement, j'ai créé une class appelée FlickrDownloader qui complète la fonctionnalité de FlickrFetcher (ainsi que mon code de caching). FlickrDownloader a cette méthode:

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

C'est essentiellement un appel à la même fonction de FlickrFetcher, mais il ajoute un callback qui est appelé à la fin du téléchargement de la photo. Dans le cas de la mise à jour de l'imageView d'un UITableViewCell, le callback ressemble à ceci:

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