Evite la animación de UICollectionView después de reloadItemsAtIndexPaths

91

UICollectionView anima los elementos después de que se llama a reloadItemsAtIndexPaths (animación atenuada).

¿Hay alguna forma de evitar esta animación?

ios 6

Marcin
fuente

Respuestas:

231

Vale la pena señalar que si tiene como objetivo iOS 7 y superior, puede usar el nuevo UIViewmétodo performWithoutAnimation:. Sospecho que debajo del capó esto está haciendo lo mismo que las otras respuestas aquí (deshabilitando temporalmente UIViewanimaciones / acciones de animación principal), pero la sintaxis es agradable y limpia.

Entonces, para esta pregunta en particular ...

C objetivo:

[UIView performWithoutAnimation:^{
    [self.collectionView reloadItemsAtIndexPaths:indexPaths];
}];


Rápido:

UIView.performWithoutAnimation {
    self.collectionView.reloadItemsAtIndexPaths(indexPaths)
}


Por supuesto, este principio se puede aplicar a cualquier situación en la que desee asegurarse de que un cambio no esté animado.

Stuart
fuente
3
Esto funcionó mejor que la respuesta aceptada para mí en iOS 7+.
Philippe Sabourin
Deshabilita todas las animaciones no solo para la vista de colección
user2159978
10
@ user2159978 Eso es correcto; todas las respuestas aquí desactivan temporalmente las animaciones de UIView. Las animaciones solo están deshabilitadas para acciones iniciadas desde dentro del bloque pasado, en este caso solo la recarga de la vista de colección. Es una respuesta perfectamente válida a la pregunta que se hace, por lo que no creo que merezca un voto negativo.
Stuart
Increible solucion. Usar esto en lugar de animateWithDuration: 0 evita un error de diseño rápido pero visible cuando se usan celdas de vista de colección de tamaño propio sin itemSize
Ethan Gill
1
Esta solución ya no funciona en iOS 14, dentro de este bloque uso reloadData, pero con iOS 13 funcionó
Maray97
161

También puedes probar esto:

UICollectionView *collectionView;

...

[UIView setAnimationsEnabled:NO];

[collectionView performBatchUpdates:^{
    [collectionView reloadItemsAtIndexPaths:indexPaths];
} completion:^(BOOL finished) {
    [UIView setAnimationsEnabled:YES];
}];

Editar:

También descubrí que si envuelve performBatchUpdatesun bloque de animación UIView, se usa la animación UIView en lugar de la animación predeterminada, por lo que puede establecer la duración de la animación en 0, así:

[UIView animateWithDuration:0 animations:^{
    [collectionView performBatchUpdates:^{
        [collectionView reloadItemsAtIndexPaths:indexPaths];
    } completion:nil];
}];

¡Esto es muy bueno si quieres usar animaciones elásticas de iOS 7 durante las inserciones y eliminaciones!

Sam
fuente
1
Gracias. Esto fue muy útil para agregar cronómetros a las celdas de vista de colección de uicollection.
hatunike
¡Brillante! Usé esto con insertCells, y con el teclado hacia arriba, para animar el collectionView a un nuevo desplazamiento después de que se insertó la celda.
David H
5
Como dice Peter, esto interfiere con otras animaciones. En su lugar, debe llamar a [UIView setAnimationsEnabled: YES] fuera del bloque de finalización. De esa forma solo evitarás esa 1 animación.
Adlai Holler
2
Agregué un método alternativo, que hace exactamente lo mismo.
Sam
1
¡Envolver el performBatchUpdatesinterior animateWithDurationes genial! ¡Gracias por el consejo!
Robert
19

UICollectionView anima los elementos después de que se llama a reloadItemsAtIndexPaths (animación atenuada).

¿Hay alguna forma de evitar esta animación?

ios 6

Supongo que estás usando un FlowLayout. Ya que está tratando de deshacerse de la animación de desvanecimiento, intente esto:

import UIKit

class NoFadeFlowLayout: UICollectionViewFlowLayout {

    override func initialLayoutAttributesForAppearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attrs = super.initialLayoutAttributesForAppearingItem(at: itemIndexPath)
        attrs?.alpha = 1.0
        return attrs
    }

    override func finalLayoutAttributesForDisappearingItem(at itemIndexPath: IndexPath) -> UICollectionViewLayoutAttributes? {
        let attrs = super.finalLayoutAttributesForDisappearingItem(at: itemIndexPath)
        attrs?.alpha = 1.0
        return attrs
    }

}

Esta es una pregunta muy antigua, por lo que probablemente ya no esté apuntando a iOS 6. Personalmente estaba trabajando en tvOS 11 y tenía la misma pregunta, así que esto está aquí para cualquiera que tenga el mismo problema.

Matt Mc
fuente
1
Hubo 3 o 4 comentarios en esta respuesta con el efecto de "¡Vaya, esto funciona muy bien!" Alguien decidió eliminar los comentarios. Creo que, de hecho, esta es la forma moderna y no pirata de lograr esto, y esos comentarios fueron un reconocimiento de esto.
Matt Mc
8

Escribí una categoría en UICollectionView para hacer precisamente eso. El truco consiste en desactivar todas las animaciones mientras se recarga:

if (!animated) {
    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
}

[self reloadItemsAtIndexPaths:indexPaths];

if (!animated) {
    [CATransaction commit];
}
Giorgio Calderolla
fuente
También recibo respuesta de Apple de que esto no debería hacer ninguna animación, si es así es un error. No sé si estoy haciendo algo mal o es un error.
Marcin
2
También puede hacerlo CATransaction.setDisableActions(true)como una abreviatura para esto.
CIFilter
5
extension UICollectionView {
    func reloadWithoutAnimation(){
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        self.reloadData()
        CATransaction.commit()
    }
}
Amjad Tubasi
fuente
esta es la sintaxis de
Swift
esto funcionó bien, incluso mejor que UIView.performWithoutAnimation
Lance Samaria
5

Aquí hay una versión de Swift 3 performBatchUpdatessin animación a UICollectionView. Descubrí que esto funcionaba mejor para mí que collectionView.reloadData()porque reducía el intercambio de celdas cuando se insertaban registros.

func appendCollectionView(numberOfItems count: Int){

        // calculate indexes for the items to be added
        let firstIndex = dataItems.count - count
        let lastIndex = dataItems.count - 1

        var indexPaths = [IndexPath]()
        for index in firstIndex...lastIndex {
            let indexPath = IndexPath(item: index, section: 0)
            indexPaths.append(indexPath)
        }

   UIView.performWithoutAnimation {

        self.collectionView.performBatchUpdates({ () -> Void in
            self.collectionView.insertItems(at: indexPaths)
        }, completion: { (finished) -> Void in

        })
    }
}
Markhorrocks
fuente
2
- (void)reloadCollectionViewAnimated:(BOOL)animated  {

    if (animated) {
        [self.collectionView performBatchUpdates:^{
            [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
        } completion:^(BOOL finished) {

        }];
    } else {
        [CATransaction begin];
        [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
        [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
        [CATransaction commit];
    }

}
Peter Lapisu
fuente
1

Solo para agregar mis $ 0.02, probé ambas versiones de la respuesta seleccionada y la forma original funcionó mejor para mis propósitos. Estoy trabajando en una vista de calendario de desplazamiento infinito que permite que un usuario ingrese al calendario en una semana determinada y luego deslice hacia adelante y hacia atrás y seleccione días individuales para filtrar una lista.

En mi implementación, para que las cosas sigan funcionando en los dispositivos más antiguos, el conjunto de fechas que representan la vista del calendario debe mantenerse relativamente pequeño, lo que significa mantener fechas de aproximadamente 5 semanas, con el usuario en el medio en la tercera semana. El problema con el uso del segundo enfoque es que hay un segundo paso en el que tienes que desplazar la vista de la colección hacia el medio sin una animación, lo que hace que la apariencia sea muy irregular por alguna razón con la animación base bloqueada.

Mi código:

[UIView setAnimationsEnabled:NO];
[self.collectionView performBatchUpdates:^{
    [self.collectionView deleteItemsAtIndexPaths:indexPathDeleteArray];
    [self.collectionView insertItemsAtIndexPaths:indexPathAddArray];

} completion:NULL];
[UIView setAnimationsEnabled:YES];

NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:14 inSection:0];
[self.collectionView scrollToItemAtIndexPath:newIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
Matt S.
fuente
0
 func reloadRowsWithoutAnimation(at indexPaths: [IndexPath]) {
        let contentOffset = collectionView.contentOffset
        UIView.setAnimationsEnabled(false)
        collectionView.performBatchUpdates {
            collectionView.reloadItems(at: indexPaths)
        }
        UIView.setAnimationsEnabled(true)
        collectionView.setContentOffset(contentOffset, animated: false)
    }
Alexandr Arsenyuk
fuente