raywenderlich animate ios animation uicollectionview uiviewanimation

ios - raywenderlich - uiview animate



Animar UICollectionView cambio de cuadro al insertar celdas (3)

Quiero cambiar el tamaño de marco de una UICollectionView en una animación que se ejecuta junto con una inserción de celdas animada en la misma vista de colección dentro de un performBatchUpdates:completion:

Este es el código que activa la inserción de la celda:

[collectionView performBatchUpdates:^{ indexPathOfAddedCell = ...; [collectionView insertItemsAtIndexPaths:@[ indexPathOfAddedCell ]]; } completion:nil];

Debido a que la inserción de celdas hace que cambie el tamaño de la vista de colección, probé el registro KVO para los cambios en esa propiedad y luego activé la actualización del marco de la vista de colección desde el controlador KVO.

El problema con este enfoque es que el desencadenante KVO para contentSize dispara demasiado tarde: la animación de inserción de celdas ya se completó en ese momento (en realidad, KVO se desencadena justo antes del controlador de finalización de performBatchUpdates:completion: se llama, pero después de que la animación se haya ejecutado en la interfaz de usuario).

No estoy usando el diseño automático.

Edit: pongo un proyecto de muestra para demostrar mi problema en GitHub.

Edición 2: debo mencionar que necesito esto para un componente que estoy escribiendo ( OLEContainerScrollView ) que se supone que es 100% independiente de la vista de colección. Debido a esto, no puedo subclasificar el diseño de vista de colección, ni tengo influencia sobre el código que activa las animaciones de celda. Idealmente, una solución también funcionaría para UITableView , que muestra el mismo comportamiento.


¿Por qué no simplemente agregar un bloque de animación para cambiar el marco de la vista de colección?
Esto produce un cambio de límites animado que va bien junto con el desvanecimiento de la inserción. El material KVO no es necesario para esta solución.

EDITAR: podría activar ese código cuando modele cambios

Aquí está el código relevante para su proyecto de muestra:

- (IBAction)addItem:(id)sender { // Change the model self.numberOfItemsInCollectionView += 1; // Animate cell insertion [self.collectionView performBatchUpdates:^{ NSIndexPath *indexPathOfInsertedCell = [NSIndexPath indexPathForItem:self.numberOfItemsInCollectionView - 1 inSection:0]; [self.collectionView insertItemsAtIndexPaths:@[ indexPathOfInsertedCell ]]; } completion:nil]; // animate the collection view''s frame [UIView animateWithDuration:.5 animations:^{ CGRect collectionViewFrame = self.collectionView.frame; collectionViewFrame.size.height = (self.numberOfItemsInCollectionView * 40) + 94; self.collectionView.frame = collectionViewFrame; }]; }


He investigado su proyecto de demostración y creo que no hay necesidad de KVO. Si desea cambiar el marco de la vista de colección con animación mientras inserta una nueva celda, creo que puede hacer algo como esto:

#import "ViewController.h" @interface ViewController () <UICollectionViewDataSource, UICollectionViewDelegate> @property (weak, nonatomic) IBOutlet UICollectionView *collectionView; @property (weak, nonatomic) IBOutlet UIView *otherView; @property (nonatomic) NSInteger numberOfItemsInCollectionView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.numberOfItemsInCollectionView = 1; // This is our model } - (IBAction)addItem:(id)sender { // Change the model self.numberOfItemsInCollectionView += 1; [UIView animateWithDuration:0.24 animations:^{ NSIndexPath *indexPathOfInsertedCell = [NSIndexPath indexPathForItem:self.numberOfItemsInCollectionView - 1 inSection:0]; [self.collectionView insertItemsAtIndexPaths:@[ indexPathOfInsertedCell ]]; CGRect collectionViewFrame = self.collectionView.frame; collectionViewFrame.size.height = (self.numberOfItemsInCollectionView * 40) + 94; self.collectionView.frame = collectionViewFrame; }]; } #pragma mark - UICollectionViewDataSource - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.numberOfItemsInCollectionView; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath; { UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyCell" forIndexPath:indexPath]; return cell; } @end

O si quieres efecto de fundido:

- (IBAction)addItem:(id)sender { // Change the model self.numberOfItemsInCollectionView += 1; CATransition *transition = [CATransition animation]; transition.type = kCATransitionFade; transition.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; transition.fillMode = kCAFillModeForwards; transition.duration = 0.5; [[self.collectionView layer] addAnimation:transition forKey:@"UICollectionViewInsertRowAnimationKey"]; NSIndexPath *indexPathOfInsertedCell = [NSIndexPath indexPathForItem:self.numberOfItemsInCollectionView - 1 inSection:0]; [self.collectionView insertItemsAtIndexPaths:@[ indexPathOfInsertedCell ]]; CGRect collectionViewFrame = self.collectionView.frame; collectionViewFrame.size.height = (self.numberOfItemsInCollectionView * 40) + 94; self.collectionView.frame = collectionViewFrame; }

He comprobado esto con su proyecto de demostración, y funciona para mí. Por favor, prueba esto y comenta si te funciona también.


Observé cómo tanto las vistas de colección como las vistas de tabla actualizan su inserción de contenido y, de hecho, el tamaño del contenido de la vista de desplazamiento solo se actualiza una vez que la animación se completa en ambos casos. No parece haber un método realmente bueno para escuchar el tamaño del contenido futuro sin usar API privada, pero es posible.

Para las vistas de tabla, el desencadenante para el inicio de la animación es -[UITableView _endCellAnimationsWithContext:] . Este método configura todas las animaciones necesarias (solo para las celdas visibles futuras), las ejecuta y establece un bloque de finalización que eventualmente llama -[UITableView _updateContentSize] . _updateContentSize usa el método interno -[UITableView _contentSize] para establecer el tamaño correcto del contenido de la vista de desplazamiento. Dado que _endCellAnimationsWithContext: trata solo con animaciones, los datos detrás de la vista de tabla ya están actualizados, por lo que llamar a _contentSize (o usar valueForKey:@"_contentSize" ) devuelve un tamaño correcto.

Es muy similar para las vistas de colección. El activador es -[UICollectionView _endItemAnimations] , inicia muchas animaciones para cada celda, encabezado y pie de página, y cuando todas las animaciones terminan, -[UICollectionView _updateAnimationDidStop:finished:context:] establece el tamaño de contenido correcto. Debido a que esta es una vista de colección, su diseño de vista de colección realmente conoce el tamaño del contenido de destino, por lo que puede llamar a -[UICollectionViewLayout collectionViewContentSize] para obtener el tamaño del contenido actualizado.

Ninguna de estas opciones es realmente buena para usar en la tienda de aplicaciones. Una opción que se me ocurre es cambiar la subclase de vista de desplazamiento ISA, y envolver todos los puntos de entrada animables, rastrear si están agrupados o no, y ya sea al final del lote o al final de la operación animada independiente, use los métodos correspondientes ( -[UITableView _contentSize] y -[UICollectionViewLayout collectionViewContentSize] ) para obtener el tamaño del contenido de destino.

Respuesta original, en caso de que desee conocer los cambios en el tamaño de una vista de colección en sus propias vistas de colección:

Subclasifique el diseño de la vista de colección (si aún no lo ha hecho) y, utilizando el centro de notificación o un método delegado, notifique en prepareForAnimatedBoundsChange: para otras animaciones. Se añadirán al bloque de animación.

De la documentación:

También puede utilizar este método para realizar animaciones adicionales. Todas las animaciones que cree se agregarán al bloque de animación utilizado para manejar las inserciones, eliminaciones y cambios de límites.

Es posible que deba determinar cuáles son los cambios y solo notificar sobre las animaciones de inserción.