Cambio de datos animados de UICollectionView

83

En mi proyecto, uso UICollectionView para mostrar una cuadrícula de iconos.

El usuario puede cambiar el orden haciendo clic en un control segmentado que solicita una recuperación de los datos centrales con diferentes NSSortDescriptor.

La cantidad de datos es siempre la misma, solo termina en diferentes secciones / filas:

- (IBAction)sortSegmentedControlChanged:(id)sender {

   _fetchedResultsController = nil;
   _fetchedResultsController = [self newFetchResultsControllerForSort];

   NSError *error;
   if (![self.fetchedResultsController performFetch:&error]) {
       NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
   }

   [self.collectionView reloadData];
}

El problema es que reloadData no anima el cambio, UICollectionView simplemente aparece con los nuevos datos.

¿Debo realizar un seguimiento de en qué indexPath estaba una celda antes y después del cambio y usar [self.collectionView moveItemAtIndexPath: toIndexPath:] para realizar la animación del cambio o hay un método mejor?

No me interesé mucho en la subclasificación de collectionViews, por lo que cualquier ayuda será excelente ...

Gracias, Bill.

Nimrod7
fuente
¿Ha intentado envolver esa reloadDatallamada en un bloque de animación?
Simon

Respuestas:

71

reloadData no se anima, ni tampoco lo hace de forma fiable cuando se coloca en un bloque de animación UIView. Quiere estar en un bloque UICollecitonView performBatchUpdates, así que intente algo más como:

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:^(BOOL finished) {
    // do something on completion 
}];
Rayas
fuente
5
sí, eso parece funcionar, pero solo si ha guardado números de sección ... de lo contrario, recibo "número de secciones no válido. El número de secciones contenidas en la vista de colección después de la actualización (10) debe ser igual al número de secciones contenidas en la vista de colección antes de la actualización (16) ". Tengo que insertar / eliminar secciones manualmente ... ¡de todos modos aceptado! Gracias.
Nimrod7
1
Por "manualmente", ¿te refieres a través de las llamadas insertSections / moveSection: toSection / deleteSections de UICollectionView? ¿O algo mas? (lo siento, hasta ahora mi uso de UICollectionView ha tenido un número estático de secciones)
Stripes
1
sí, a través de esos métodos ... es un poco complicado implementar el pensamiento, debe recordar las rutas de índice antes y después de la actualización, la figura que eliminó, insertó o movió ...
Nimrod7
12
Esto no funcionó para mi vista de colección de una sección. No obtuve errores, pero tampoco se animó. La sustitución -reloadSections:hace que funcione.
Paulmelnikow
9
Esta respuesta no es correcta, ya que en mi caso, poner reloadData en performBatchUpdates bloqueará mi aplicación y dirá que el recuento de celdas no es el mismo antes y después. el uso de reloadSections resolvió mi problema. El documento de Apple también indica no llamar a reloadData en el bloque de animación
Wingzero
145

Envolver -reloadDataen -performBatchUpdates:no parece causar una vista de colección de una sola sección para animar.

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadData];
} completion:nil];

Sin embargo, este código funciona:

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];
Paulmelnikow
fuente
9
Nota de la documentación: No debe llamar al método de recarga de datos en medio de bloques de animación donde se insertan o eliminan elementos. Las inserciones y eliminaciones hacen que los datos de la tabla se actualicen automáticamente de manera adecuada.
Raphael Oliveira
Creo que esta debería ser la respuesta correcta. porque esto funcionó para mí. Solo tengo 1 sección y esta no funciona, no es la respuesta correcta.
Mahesh Agrawal
No funciona si también está insertando. Anulará la animación de clasificación.
Andrés Canella
3
Vale la pena señalar que no necesita la self.collectionView performBatchUpdatesllamada envuelta reloadSectionspara obtener la animación. De hecho, la performBatchUpdatesllamada puede causar problemas inadvertidos de parpadeo / cambio de tamaño de celda en algunos casos.
Paul Popiel
1
O simplemente [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]]; reloadSectionsen la vista de colección o en la vista de tabla lo hará con animación.
bauerMusic
67

Esto es lo que hice para animar la recarga de TODAS LAS SECCIONES:

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)]];

Swift 3

let range = Range(uncheckedBounds: (0, collectionView.numberOfSections))
let indexSet = IndexSet(integersIn: range)
collectionView.reloadSections(indexSet)
Yariv Nissim
fuente
8

Para los usuarios de Swift, si su vista de colección solo tiene una sección:

self.collectionView.performBatchUpdates({
                    let indexSet = IndexSet(integersIn: 0...0)
                    self.collectionView.reloadSections(indexSet)
                }, completion: nil)

Como se ve en https://stackoverflow.com/a/42001389/4455570

Gabriel Wamunyu
fuente
3

Recargar la vista de la colección completa dentro de un performBatchUpdates:completion:bloque produce una animación con fallas para mí en el simulador de iOS 9. Si tiene un específico UICollectionViewCellque desea eliminar, o si tiene su ruta de índice, puede llamar deleteItemsAtIndexPaths:a ese bloque. Al usarlo deleteItemsAtIndexPaths:, hace una animación suave y agradable.

UICollectionViewCell* cellToDelete = /* ... */;
NSIndexPath* indexPathToDelete = /* ... */;

[self.collectionView performBatchUpdates:^{
    [self.collectionView deleteItemsAtIndexPaths:@[[self.collectionView indexPathForCell:cell]]];
    // or...
    [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:nil];
justicia
fuente
1
Solo quiero mencionar algo que resolvió el problema de bloqueo en mi caso, actualice la fuente de datos (es decir, el recuento de secciones / elementos) antes de llamar a performUpdates en la vista de colección.
ViruMax
La animación es de las otras operaciones; hay, y nunca hubo BAJO NINGUNA CIRCUNSTANCIA, una animación para reloadData. insertItems se inserta en orden ascendente y se realiza DESPUÉS de las actualizaciones; deleteItems es lo contrario.
James Bush
1

El texto de ayuda dice:

Llame a este método para volver a cargar todos los elementos en la vista de colección. Esto hace que la vista de colección descarte los elementos actualmente visibles y los vuelva a mostrar. Para mayor eficiencia, la vista de colección solo muestra aquellas celdas y vistas complementarias que son visibles. Si los datos de la colección se reducen como resultado de la recarga, la vista de la colección ajusta sus desplazamientos de desplazamiento en consecuencia. No debe llamar a este método en medio de bloques de animación donde se insertan o eliminan elementos. Las inserciones y eliminaciones hacen que los datos de la tabla se actualicen automáticamente de manera adecuada.

Creo que la parte clave es "hace que la vista de colección descarte cualquier elemento actualmente visible". ¿Cómo va a animar el movimiento de los elementos que ha descartado?

Víctor Engel
fuente
Exactamente, no lo es. reloadData es una operación dual sin animación; primero es una operación de eliminación y luego una operación de inserción.
James Bush