UICollectionView recarga imágenes incorrectas en desplazamiento

En el siguiente código, descargo URL de image de un file de text, lo almaceno en un NSMutableArray y luego descargo las imágenes desde esas URL en CellforItemAtIndexPath. Sin embargo, cuando me desploop hacia arriba y hacia abajo, por un breve momento, la image incorrecta está en el lugar equivocado. Corrige un segundo más tarde, pero me gustaría deshacerme de ese problema de desplazamiento. Aquí está el código:

- (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:@"networking.png"]]; } else { NSLog(@"noimage"); recipeImageView.image = [UIImage imageNamed:@"networking.png"]; } titleView.text = hi[indexPath.row]; }); }); return cell; // recipeImageView.image = [UIImage imageNamed:[videoList objectAtIndex:indexPath.row]]; } 

¿Algunas ideas? Gracias.

Noté un montón de votos negativos sin comentarios y después de leer mi respuesta nuevamente creo que la respuesta original aceptada (debajo de la línea) podría haber sido mejor.

Hay algunas cosas que suceden con este tipo de problemas. La UITableView solo crea lo suficiente UITableViewCells para mostrar cada celda a la vista más lo que se va a desplazar a la vista. Las otras celdas se volverán a usar después de que se desplacen fuera de la vista.

Esto significa que cuando te desplazas rápido, la celda se configura varias veces para get una image asincrónica y luego mostrarla porque es bastante lenta para get la image. Después de que la image se cargue, se mostrará en la celda que originalmente llamó para searchla, pero esa celda podría volver a usarse.

Hoy en día tengo un buffenetworkingImage: UIImage? propiedad en cualquier class de datos que use como datos para UITableViewCells. Esto inicialmente será siempre nulo, así que searché la image asíncrona y cuando la synchronization de dispatch regrese la almacenará allí, así que la próxima vez que necesite mostrar esta celda esté list, y si sigue siendo la misma celda, también la mostraré en la celda .

Entonces, la forma correcta de hacerlo es:

  • Comienza a configurar la celda con tu object de datos
  • Almacena el object de datos en tu celular para que pueda ser referido más tarde.
  • Compruebe si el object de datos ya tiene una image establecida en buffenetworkingImage y, si es así, visualícela y eso es todo
  • Si todavía no hay image, restablezca la image actual a nada o un marcador de position y comience a download la image en segundo plano
  • Cuando la llamada asincrónica devuelve almacenar la image en la propiedad buffenetworkingImage
  • Compruebe si el object que está almacenado en la celda actual sigue siendo el mismo object para el que buscó la image ( esto es muy importante ), si no no haga nada
  • Si todavía está en la misma celda, visualice la image

Entonces, hay un object que pasas en tu llamada asincrónica para que puedas almacenar la image buscada. Hay otro object en la celda actual con el que puedes comparar. A menudo, la célula ya se reutilizó antes de get la image, por lo que esos objects son diferentes en el momento en que se descarga la image.


Desplazamiento y rotation de CollectionViews siempre ha sido un poco problemático. Siempre tengo el mismo problema, las imágenes que aparecen en las celdas incorrectas. Existe algún tipo de optimization para la vista de colección que afecta a las imágenes, pero no a las tags. Para mí es un error, pero aún no se resuelve después de tres versiones de iOS.

Necesita implementar lo siguiente:

https://developer.apple.com/library/ios/documentation/uikit/reference/UICollectionViewLayout_class/Reference/Reference.html#//apple_ref/occ/instm/UICollectionViewLayout/shouldInvalidateLayoutForBoundsChange :

Esto siempre debería devolver SÍ básicamente para actualizarlo siempre. Si esto resuelve su problema, entonces podría tratar de no ejecutar todo el nuevo dibujo cada vez, ya que es malo para el performance. Personalmente, dediqué mucho time a contar si había pasado una pantalla o si había pasado una línea y así sucesivamente, pero en lugar de eso, terminé volviendo a SÍ. YMMV.

No estoy seguro de qué es AsyncImageView , así que primero déjame responder tu pregunta como si fuera un UIImageView :


En su bloque de callback (cuando regresa a la queue principal), se refiere a recipeImageView . Sin embargo, si el usuario se ha desplazado durante la descarga, el object de cell se ha reutilizado.

Una vez que está en la callback, debe asumir que todas las references a las vistas ( cell , recipeImageView , blurView , etc.) apuntan a lo incorrecto. blurView search las vistas correctas utilizando su model de datos.

Un enfoque común es usar NSIndexPath para encontrar la celda:

 UICollectionViewCell *correctCell = [collectionView cellForItemAtIndexPath:indexPath]: UIImageView *correctRecipeImageView = (UIImageView *)[correctCell viewWithTag:100]; /* Update your image now… */ 

Tenga en count que esto solo funcionará si un indexPath siempre está garantizado para apuntar al mismo contenido: si el usuario puede insert / eliminar filas, entonces necesitará averiguar el path index correcto también, ya que también puede haber cambiado:

 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… */ 

Dicho esto, si AsyncImageView es alguna otra class que supuestamente debe manejar todo esto por usted, entonces puede llamar a setImageWithURL: placeholderImage: en el hilo principal, y debe manejar toda la carga asíncrona en su nombre. Si no es así, me gustaría recomendar la biblioteca SDWebImage, que hace: https://github.com/rs/SDWebImage

Solucioné el problema descargando NSArray al iniciar la aplicación para que se almacenara localmente.