2009-11-15 11 views
1

J'ai dessiné des cellules de tableau personnalisées (en utilisant les échantillons d'Apple comme base) et je dois maintenant faire une cellule qui affiche une image à partir d'une URL - chaque cellule aurait une image différente (basée sur certaines données il a) mais toutes les cellules sont les mêmes et donc le même id de réutilisation.iPhone personnalisé dessiné UITableViewCell, comment charger + insérer des images du Web dans un fil?

Quelle est la structure correcte pour cela? Évidemment j'ai besoin de charger l'image dans un nouveau fil. J'ai la fonction suivante si bien assis dans les cellules classe voir qui est exécutée dans son propre thread:


- (void)loadImage 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    self.img = [UIImage imageWithData: [NSData dataWithContentsOfURL: [NSURL URLWithString: [myProduct objectForKey:@"ImagePath"]]]]; 
    [self setNeedsDisplay]; 
    [pool release]; 
} 
 

Quand j'appelle cela de la fonction drawRect lui-même (ce qui est mauvais) puis elle « fonctionne », mais cela est évidemment appelé chaque fois que quelque chose se passe (sélection, etc.). Si je le mets dans la fonction init des cellules uiview, alors il n'est appelé que pour les 8 premières cellules et ensuite elles sont réutilisées. D'autres variations ont fini par faire que l'image ne soit pas 'réinitialisée' quand la cellule est réutilisée et donc les mêmes 8 images se répètent dans la table (bien que l'autre texte soit mis à jour).

Je ne m'inquiète pas de la mise en cache pour l'instant, mais y a-t-il des exemples de comment faire cela, ou est-ce que quelqu'un peut me pointer dans la bonne direction? Merci.

Répondre

0

Eh bien, lorsque vous déposez la cellule, vous pouvez forcer le chargement de l'image là-bas. Vous ne devez pas le faire dans init ... En général, vous faire:

cell = dequeueCell... 
if (!cell) { 
    //create one 
} 

return cell; 

Qu'est-ce que vous pouvez faire est avant que la cellule de retour, vous pouvez forcer le chargement d'image si elle est le type de cellule que vous voulez . De cette façon, à chaque fois qu'il s'affiche, vous pouvez l'extraire de votre cache (s'il existe) et si ce n'est pas le cas, allez sur le Web.

+0

Comment est-ce que je manipulerais les bits de fil à ceci? Je peux appeler le thread dans cellforrowatindexpath qui va et récupère l'image, mais à ce moment-là la cellule aura déjà dessiné (moins l'image), puis j'aurais besoin d'appeler setNeedsDisplay sur la vue des cellules à nouveau? – Tom

2

La solution va devoir être plus sophistiquée car, par défaut, UITableView recycle les cellules (quand on fait défiler le haut, il est déplacé vers le bas et reconfiguré avec de nouvelles données). Par conséquent, il est possible que vous commenciez à télécharger une image, mais le contenu de la cellule est modifié avant la fin du téléchargement.

Dans cellForRowAtIndexPath:, vous devriez obtenir l'image à partir d'un cache (votre objet myProduct, un NSArray, peu importe). Si ce n'est pas le cas, vous devriez vérifier un drapeau pour voir s'il est déjà chargé. S'il n'est pas déjà chargé, alors vous définissez ce drapeau et détachez un nouveau thread (vous devriez regarder dans NSOperation, il fera la queue le travail et vous empêchera de lancer trop de threads à la fois).

Dans votre thread, vous devez télécharger l'image, puis utiliser performSelectorOnMainThread:target:waitUntilDone: pour appeler une méthode sur le thread principal avec l'image. Cette méthode peut mettre à jour le cache, définir l'indicateur isLoading sur NO et mettre à jour la cellule. Il est important de le faire de cette façon, car Cocoa exige que tout votre code de mise à jour de l'interface utilisateur soit sur le thread principal. J'espère que c'est un aperçu utile de ce qu'il faut faire.

+0

Merci, cela a aidé. J'ai jeté un coup d'oeil sur le code threadview flickr de la série stanford qui m'a aidé aussi. Je l'ai fait fonctionner en faisant cell.image = [cachedImg: url] qui ensuite déclenche le NSOperation puis fait un reloadData sur le tableview une fois terminé, mais j'aimerais bien utiliser le dessin personnalisé comme avec le reste de la cellule -comment j'irais à ce sujet? Je devrais passer l'image à la classe de cellule, qui la passe à sa classe de vue, puis appelle setNeedsDisplay? Comment puis-je référencer la cellule d'origine dans la méthode appelée par performSelectorOnMainThread? – Tom

+1

Je trouve une très bonne approche à cela est d'utiliser NSOperations en combinaison avec NSNotifications - vous pouvez avoir une cellule souscrite à une notification pour le chargement de l'image que la charge complète de l'image se déclenche. Vous pouvez joindre quelque chose comme l'URL de l'image pour vous assurer que la notification que vous recevez correspond à l'image que vous attendez, et vous désinscrire dans prepareForReuse afin que seules les cellules visibles soient modifiées lorsque la notification est envoyée. –

1

Si vous ne l'avez pas vu une documentation explicite d'Apple à l'effet que je UIImage est don de thread-safe Je pense que tu devrais faire ça. Au lieu de cela, obtenez votre image en tant que NSData et transformez-la en UIImage dans votre thread principal. La règle de base est que tout le travail UIKit DOIT être fait dans votre thread principal ou le comportement est indéfini.