Dans des applications telles que Youtube ou même l’Appstore, il est fréquent de voir des UITableViewController affichant des images thumbnails. Ces images sont chargées de manière asynchrone, afin de ne pas bloquer l’UI pendant le chargement, lorsque vous scrollez dans la table. Typiquement, pour arriver à ce résultat, il faut créer un nouveau thread (NSThread ou NSOperation/NSOperationQueue) pour chaque téléchargement d’image, et renvoyer le résultat au main thread une fois terminé, pour procéder à l’affichage.
Dans un récent article, Mark Johnson propose une solution, simple et efficace, qui évite de devoir créer et gérer les threads manuellement (ce qui peut devenir complexe assez rapidement). Elle se base sur l’objet NSURLConnection, permettant de charger le contenu d’une URL, de manière asynchrone. L’image téléchargée est ajoutée comme subView de notre UITableViewCell, au lieu d’utiliser la propriété image de la cellule.
L’idée proposée par Mark est basée sur une subclass de UIView.  Je vous propose ici ma version, reprenant son idée, mais se basant sur un subclassing de UIImageView, encore plus simple à mettre en place.
AsyncUIImageView.h
1 2 3 4 5 6 7 8 9 10 | #import <Foundation/Foundation.h> @interface AsyncUIImageView : UIImageView { NSURLConnection* connection; NSMutableData* data; } - (void)loadImageFromURL:(NSURL*)url; @end |
AsyncUIImageView.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #import "AsyncUIImageView.h" @implementation AsyncUIImageView - (void)loadImageFromURL:(NSURL*)url { if (connection != nil) { [connection release]; } if (data != nil) { [data release]; } NSURLRequest* request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; if (connection) { data = [[NSMutableData data] retain]; } else { //TODO error handling. } } - (void)connection:(NSURLConnection *)theConnection didReceiveData:(NSData *)incrementalData { [data appendData:incrementalData]; } - (void)connectionDidFinishLoading:(NSURLConnection*)theConnection { [connection release]; connection = nil; self.image = [UIImage imageWithData:data]; self.contentMode = UIViewContentModeScaleAspectFit; self.autoresizingMask = ( UIViewAutoresizingFlexibleWidth || UIViewAutoresizingFlexibleHeight ); [self setNeedsLayout]; [data release]; data = nil; } - (void)dealloc { [connection cancel]; [connection release]; [data release]; [super dealloc]; } @end |
Dans votre UITableViewController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease]; } else { AsyncUIImageView* oldImage = (AsyncUIImageView*)[cell.contentView viewWithTag:999]; [oldImage removeFromSuperview]; } AsyncUIImageView* asyncImage = [[[AsyncUIImageView alloc] initWithFrame:CGRectMake(0,0,75,75)] autorelease]; asyncImage.tag = 999; NSURL* url = [[NSURL alloc] initWithString: [myImagesArray objectAtIndex:indexPath.row]]; [asyncImage loadImageFromURL:url]; [cell.contentView addSubview:asyncImage]; return cell; } |
Pour parfaire cette solution, il reste à :
- mettre en place un système de cache des images téléchargées
- gérer les éventuels problèmes de connexion dans notre AsyncUIImageView
Thanks Mark!



4 commentaire(s)
Thanks Mark!
You have done great job!
If you’d like to thank Mark, the original author of this idea, you can thank him directly on his blog
http://www.markj.net/iphone-asynchronous-table-image/
Merci pour ce tuto mais je n’arrive pas à le faire fonctionner, j’ai plusieurs erreur (Peut tu m’expliquer comment créer « myImagesArray »)
myImagesArray est simplement un tableau (NSArray ou NSMutableArray) contenant les URL des images pour chaque row de ta tableView, que tu pourras par exemple créer dans ta méthode loadView.