J'ai écrit deux méthodes pour charger de manière asynchrone des images dans la cellule de mon UITableView. Dans les deux cas, l'image se charge correctement mais lorsque je fais défiler le tableau, les images changent plusieurs fois jusqu'à ce que le défilement se termine et que l'image revienne à la bonne image. Je n'ai aucune idée de la raison pour laquelle cela se produit.
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:
@"http://myurl.com/getMovies.php"]];
[self performSelectorOnMainThread:@selector(fetchedData:)
withObject:data waitUntilDone:YES];
});
}
-(void)fetchedData:(NSData *)data
{
NSError* error;
myJson = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
[_myTableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
// Return the number of rows in the section.
// Usually the number of items in your array (the one that holds your list)
NSLog(@"myJson count: %d",[myJson count]);
return [myJson count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (cell == nil) {
cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
dispatch_async(kBgQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.poster.image = [UIImage imageWithData:imgData];
});
});
return cell;
}
... ...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (cell == nil) {
cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * response,
NSData * data,
NSError * error) {
if (!error){
cell.poster.image = [UIImage imageWithData:data];
// do whatever you want with image
}
}];
return cell;
}
6 votes
Vous essayez de stocker des informations dans les cellules actuelles. C'est mauvais, très mauvais. Vous devez stocker les informations dans un tableau (ou quelque chose de similaire) et les afficher ensuite dans les cellules. Dans ce cas, l'information est l'UIImage réelle. Oui, chargez-la de manière asynchrone, mais chargez-la dans un tableau.
1 votes
@Fogmeister Faites-vous référence à
poster
? Il s'agit vraisemblablement d'une image dans sa cellule personnalisée, donc ce que fait EXEC_BAD_ACCESS est parfaitement correct. Vous avez raison de dire que vous ne devez pas utiliser la cellule comme référentiel pour les données du modèle, mais je ne pense pas que ce soit ce qu'il fait. Il donne simplement à la cellule personnalisée ce dont elle a besoin pour se présenter. En outre, et c'est une question plus subtile, je serais prudent quant au stockage d'une image, elle-même, dans votre tableau de modèle soutenant votre tableview. Il est préférable d'utiliser un mécanisme de mise en cache de l'image et votre objet modèle doit récupérer à partir de ce cache.1 votes
Oui, c'est exactement ce que je veux dire. En regardant la requête (qui est affichée en entier), il télécharge l'image de manière asynchrone et la place directement dans l'imageView de la cellule. (Il utilise donc la cellule pour stocker les données, c'est-à-dire l'image). Ce qu'il devrait faire, c'est référencer un objet et demander l'image à cet objet (contenu dans un tableau ou autre). Si l'objet n'a pas encore l'image, il devrait renvoyer un espace réservé et télécharger l'image. Ensuite, lorsque l'image est téléchargée et prête à être affichée, il faut le faire savoir au tableau pour qu'il puisse mettre à jour la cellule (si elle est visible).
1 votes
Ce qu'il fait forcera le téléchargement chaque fois qu'il fera défiler cette cellule dans le tableau. C'est à lui de décider si les images sont stockées de manière persistante, mais il faut au moins les stocker pendant toute la durée de vie de la vue du tableau.
0 votes
Donc, ce que je devrais faire, c'est de mettre à l'intérieur
tableView:cellForRowAtIndexPath
obtenir l'image courante à partir de l'objectAtIndex de indexPath.row, s'il est vide, faire la requête et télécharger l'image à cet endroit de la zone. La prochaine fois que la cellule sera visible, l'image sera chargée depuis le tableau. Quelque chose comme ça ? Comme @rob l'a suggéré, AFNetworking me donne une solution complète pour les problèmes liés au téléchargement et au cache, mais vos commentaires sont toujours très intéressants.1 votes
Exactement :D De cette façon, vous ne devez récupérer l'image de l'URL qu'une seule fois. Vous verrez cela sur des choses comme Facebook Friend Picker. Lorsque vous le lancez, tous les avatars sont des espaces vides gris. Puis, lorsque vous faites défiler la page, ils se remplissent tous au fur et à mesure. Mais lorsque vous revenez à une cellule précédemment affichée, l'image déjà téléchargée s'affiche instantanément.
0 votes
Oui, j'ai modifié mon dernier commentaire. J'utilise déjà le cache dans ma solution AFNetworking actuelle. Merci beaucoup pour vos commentaires !
0 votes
@Fogmeister Merci. Je suis d'accord avec votre diagnostic, à savoir que son code actuel émettrait des requêtes réseau redondantes (d'où la suggestion qu'il utilise
SDWebImage
ouAFNetworking
qui sont toutes deux en cachette). Je m'opposais seulement à la caractérisation de son approche selon laquelle il utilisait la cellule pour "stocker" l'image (parce que ce n'est pas le cas ; il a en fait le problème inverse, à savoir qu'il ne stocke l'image nulle part et qu'il la jette lorsque la cellule est réutilisée). Donc, bien que je puisse ergoter sur la sémantique, je pense que nous sommes d'accord sur la solution de base.0 votes
S'il vous plaît, regardez mon message dans une autre question : stackoverflow.com/a/26147814/3052059
0 votes
J'ai un très bon exemple de la façon de gérer cela dans UIImageLoader. Non seulement vous pouvez gérer ce cas très facilement, mais UIImageLoader est un cache, donc les images qui ont déjà été téléchargées seront immédiatement disponibles sur le disque ou en mémoire. github.com/gngrwzrd/UIImageLoader
0 votes
@Rob Salut Rob ! Je comprends que les bibliothèques sont excellentes pour ce problème, mais j'ai besoin d'apprendre cela. Pouvez-vous jeter un coup d'œil à cette question s'il vous plaît, merci : stackoverflow.com/questions/44743591/