10
votes

UicollectionView recharge de mauvaises images sur le défilement

Dans le code ci-dessous, je télécharge des URL de l'image à partir d'un fichier texte, stockez-la dans un NSMutableArry, puis téléchargez les images de ces URL dans CellForitematidexPath. Cependant, lorsque je fais défiler de haut en bas, pour un bref moment, la mauvaise image est au mauvais endroit. Il corrige une seconde plus tard, mais je voudrais me débarrasser de ce problème de défilement. Voici le code:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *identifier = @"Cell2";

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
    AsyncImageView *recipeImageView = (AsyncImageView *)[cell viewWithTag:100];
    UILabel *titleView = (UILabel *)[cell viewWithTag:101];
    titleView.numberOfLines = 3;
    UIImageView *overlay = (UIImageView *)[cell viewWithTag:111];
    FXBlurView *blurView = (FXBlurView *)[cell viewWithTag:110];
    blurView.tintColor = [UIColor colorWithRed:51.0 green:51.0 blue:51.0 alpha:1];


    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        hi = [self videoTitle];
        images = [self firstPhoto];
        // switch to a background thread and perform your expensive operation


        // switch back to the main thread to update your UI


        dispatch_async(dispatch_get_main_queue(), ^{
            NSString *imgSrc;

            NSString *url = images[indexPath.row];
            imgSrc = url;
            if (imgSrc != nil && [imgSrc length] != 0 ) {
                [recipeImageView setImageWithURL:[NSURL URLWithString:images[indexPath.row]] placeholderImage:[UIImage imageNamed:@"red.png"]];

            }
            else {
                NSLog(@"noimage");
                recipeImageView.image = [UIImage imageNamed:@"red.png"];
            }
            titleView.text = hi[indexPath.row];


        });
    });

    return cell;


    // recipeImageView.image = [UIImage imageNamed:[videoList objectAtIndex:indexPath.row]];

}


0 commentaires

3 Réponses :


6
votes

Je ne suis pas sûr de ce que asyncimageview code> est, alors laissez-moi d'abord répondre à votre question comme s'il s'agissait d'un uIImageView code>:

dans votre bloc de rappel (Lorsque vous revenez à la file d'attente principale), vous vous référez à recûteImageView code>. Toutefois, si l'utilisateur a défilé pendant le téléchargement, l'objet Cell CODE> a été réutilisé. P>

Une fois dans le rappel, vous devez supposer que toutes vos références à des vues ( cellule code>, recetteImageView code>, bluView code>, etc., pointez sur la mauvaise chose. Vous devez trouver em> le bon Vues à l'aide de votre modèle de données. P>

Une approche commune utilise le nsindexpath pour trouver la cellule: p> xxx pré>

Notez que cela ne fonctionnera que si un IndexPath code> est toujours garanti pour pointer sur le même contenu - si l'utilisateur peut insérer / supprimer des lignes, vous devez également comprendre la bonne index de l'index, car elle peut aussi être modifiée: P>

NSIndexPath *correctIndexPath  = /* Use your data model to find the correct index path */
UICollectionViewCell *correctCell = [collectionView cellForItemAtIndexPath:correctIndexPath]:
UIImageView *correctRecipeImageView = (UIImageView *)[correctCell viewWithTag:100];
/* Update your image now… */


4 commentaires

Alors devrait-il être comme ça? Pastie.org/8751762 . Cela ne fonctionne pas, il a toujours le même problème (je viens de le faire un uiImageView).


SDwebimage offres mieux avec ce problème de défilement. Merci @aaronBroder


N'utilisez jamais de la vueWithTag, c'est trop fragile. Au moins pas lorsque votre cellule pourrait avoir une @iboutlet à la place.


@DePartamentOB Vous avez raison que le sous-classement et les propriétés constituent une meilleure solution, mais dans ce cas, l'OP n'est pas la sous-classement Uicollectionviewcell, et la solution que j'ai affichée est la plus petite modification nécessaire pour le faire fonctionner. Je ne pense pas que je conviens que vous devriez "jamais" utiliser cette technique



2
votes

J'ai remarqué un tas de bowvotes sans commentaires et après avoir lu ma réponse, je suppose que la réponse acceptée originale (sous la ligne) aurait pu être meilleure.

Il y a quelques points à venir avec ces types de problèmes. L'UitailView ne crée que suffisamment d'utilviewcells pour afficher toutes les cellules en vue et tout ce qui va faire défiler la vue. Les autres cellules vont être réutilisées après leur défilement hors de vue.

Cela signifie que lorsque vous faites défiler rapidement, la cellule est configurée plusieurs fois pour aller chercher une image ASYNC, puis l'afficher car il est assez lent à récupérer l'image. Après la charge de l'image, il se montrera sur la cellule qui a appelé à l'origine pour le chercher, mais cette cellule pourrait être réutilisée déjà.

De nos jours, j'ai une bufferedimage: UiImage? Propriété sur n'importe quelle classe de données que j'utilise comme données pour les UitablesViewCells. Cela sera toujours toujours nulle donc je vais chercher l'image ASYNC et lorsque la DISPATCH_ASYNC revient, elle le stockera ainsi la prochaine fois que je dois montrer cette cellule, elle est prête, et si c'est toujours la même cellule, je l'afficherai également dans la cellule. .

Donc, la bonne façon de le faire est:

  • Commencez à configurer la cellule avec votre objet de données
  • Stockez l'objet de données dans votre cellule afin qu'il puisse être référé plus tard
  • Vérifiez si l'objet Data a déjà une image définie dans BuffereDimage et si cela l'affiche et c'est ce qu'il est
  • S'il n'y a pas encore d'image, réinitialisez l'image actuelle sur rien ni un espace réservé et commencez à télécharger l'image en arrière-plan
  • Lorsque l'appel ASYNC renvoie stocker l'image dans la propriété BuffereDimage
  • Vérifiez si l'objet stocké dans la cellule actuelle est toujours le même objet que vous avez récupéré l'image pour ( Ceci est très important ), sinon ne rien faire
  • Si vous êtes toujours dans la même cellule Afficher l'image

    Il y a donc un objet que vous passez dans votre appel ASYNC afin que vous puissiez stocker l'image récupérée. Il y a un autre objet qui est dans la cellule actuelle que vous pouvez comparer. Souvent, la cellule s'est déjà réutilisée avant d'avoir l'image, de sorte que ces objets sont différents au moment de la téléchargement de votre image.


    Collection de défilement et de rotation de rotation a toujours été un peu problématique. J'ai toujours le même problème, des images qui apparaissent dans les mauvaises cellules. Il y a une sorte d'optimisation pour la collectionView qui affecte les images mais pas les étiquettes. Pour moi, c'est un virus, mais cela n'est toujours pas résolu après trois versions iOS.

    Vous devez implémenter les éléments suivants:

    https://developer.apple.com/library/ios/documentation/uikit/reference/uicollectionviewlayout_class/reference/reference.html#//apple_ref/occ/instm/ucan3lectionvalidatelayoutforboundschangeur :

    Cela devrait toujours revenir oui essentiellement pour toujours l'actualiser. Si cela résout votre problème, vous pouvez rechercher sans exécuter tout le redessinement à chaque fois car il est mauvais pour la performance. J'ai personnellement passé beaucoup de temps à compter si un écran était passé ou une ligne était passé et ainsi de suite, mais il est devenu sourdy plutôt que j'ai fini par retourner oui. Ymmv.


0 commentaires

0
votes

Je vois que vous avez déjà répondu à cette question, mais je veux toujours partager mon expérience.

J'ai eu le même problème.

Dans mon cas, la raison de ce problème était la fonction suivante: Collectionview.dequeueeueAreSablecell (...).

Le type de cellules était identique dans tous les cas, mais les données étaient différentes:

  • Dans certains cas, des données pour une cellule spécifique contenaient une image.
  • Dans certains cas, des données pour une cellule spécifique ne contiennent pas d'image, c'était nul.

    Quand l'image était nulle, je ne définissait pas une image dans l'imageView. Dans une telle affaire, la vue de collecte réutilise une image de l'autre cellule (image incorrecte pour une cellule en cours).

    solution est très simple: il suffit de définir nil, si imageview ne contient pas d'image.

    J'espère que cela vous aidera.


0 commentaires