Éviter l'animation de UICollectionView après reloadItemsAtIndexPaths

91

UICollectionView anime les éléments après l'appel de reloadItemsAtIndexPaths (animation de fondu).

Existe-t-il un moyen d'éviter cette animation?

iOS 6

Marcin
la source

Réponses:

231

Il est à noter que si vous ciblez iOS 7 et supérieur, vous pouvez utiliser la nouvelle UIViewméthode performWithoutAnimation:. Je soupçonne que sous le capot, cela fait à peu près la même chose que les autres réponses ici (désactivation temporaire des UIViewanimations / actions Core Animation), mais la syntaxe est agréable et propre.

Donc pour cette question en particulier ...

Objectif c:

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


Rapide:

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


Bien entendu, ce principe peut s'appliquer à toute situation dans laquelle vous souhaitez vous assurer qu'un changement n'est pas animé.

Stuart
la source
3
Cela a mieux fonctionné que la réponse acceptée pour moi sur iOS 7+.
Philippe Sabourin
Il désactive toutes les animations non seulement pour la vue de collection
user2159978
10
@ user2159978 C'est exact; toutes les réponses ici désactivent temporairement les animations UIView. Les animations ne sont désactivées que pour les actions initiées à partir du bloc transmis, dans ce cas uniquement le rechargement de la vue de collection. C'est une réponse parfaitement valable à la question posée, donc je ne pense pas qu'elle mérite un vote défavorable.
Stuart
Solution incroyable. L'utilisation de ceci au lieu de animateWithDuration: 0 évite un problème de mise en page rapide mais visible lors de l'utilisation de cellules de vue de collection auto-dimensionnantes sans itemSize
Ethan Gill
1
Cette solution ne fonctionne plus sur iOS 14, à l'intérieur de ce bloc, j'utilise reloadData, mais avec iOS 13, cela a fonctionné
Maray97
161

Vous pouvez également essayer ceci:

UICollectionView *collectionView;

...

[UIView setAnimationsEnabled:NO];

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

Éditer:

J'ai également constaté que si vous enveloppez performBatchUpdatesun bloc d'animation UIView, l'animation UIView est utilisée à la place de l'animation par défaut, vous pouvez donc simplement définir la durée de l'animation sur 0, comme ceci:

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

C'est très cool si vous souhaitez utiliser des animations élastiques iOS 7 lors des insertions et des suppressions!

Sam
la source
1
Merci. Cela a été très utile pour ajouter des minuteries de chronomètre aux cellules uicollectionview.
hatunike
Brillant! Je l'ai utilisé avec insertCells, et avec le clavier vers le haut, pour animer la collectionView à un nouveau décalage après l'insertion de la cellule.
David H du
5
Comme le dit Peter, cela interfère avec d'autres animations. Au lieu de cela, vous devez appeler [UIView setAnimationsEnabled: YES] en dehors du bloc d'achèvement. De cette façon, vous n'empêchez qu'une seule animation.
Adlai Holler
2
J'ai ajouté une méthode alternative, qui fait exactement la même chose.
Sam
1
Emballer à l' performBatchUpdatesintérieur animateWithDurationest génial! Merci pour le conseil!
Robert
19

UICollectionView anime les éléments après l'appel de reloadItemsAtIndexPaths (animation de fondu).

Existe-t-il un moyen d'éviter cette animation?

iOS 6

Je suppose que vous utilisez un FlowLayout. Puisque vous essayez de vous débarrasser de l'animation de fondu, essayez ceci:

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
    }

}

C'est une question très ancienne, donc vous ne ciblez probablement plus iOS 6. Je travaillais personnellement sur tvOS 11 et j'avais la même question, c'est donc ici pour tous ceux qui rencontrent le même problème.

Matt Mc
la source
1
Il y a eu 3 ou 4 commentaires sur cette réponse à l'effet "Wow ça marche vraiment bien!" Quelqu'un a décidé de supprimer les commentaires. Je pense, en fait, que c'est la manière moderne et non hacky d'accomplir cela, et ces commentaires en étaient la reconnaissance.
Matt Mc
8

J'ai écrit une catégorie sur UICollectionView pour faire exactement cela. L'astuce consiste à désactiver toutes les animations lors du rechargement:

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

[self reloadItemsAtIndexPaths:indexPaths];

if (!animated) {
    [CATransaction commit];
}
Giorgio Calderolla
la source
Je reçois également une réponse d'Apple que cela ne devrait pas faire d'animation, si c'est le cas, c'est un bogue. Je ne sais pas si je fais quelque chose de mal ou si c'est un bug.
Marcin
2
Vous pouvez également simplement faire CATransaction.setDisableActions(true)un raccourci pour cela.
CIFilter
5
extension UICollectionView {
    func reloadWithoutAnimation(){
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        self.reloadData()
        CATransaction.commit()
    }
}
Amjad Tubasi
la source
c'est la syntaxe Swift 3
Amjad Tubasi
cela a bien fonctionné, encore mieux que UIView.performWithoutAnimation
Lance Samaria
5

Voici une version Swift 3 performBatchUpdatessans animation vers un fichier UICollectionView. J'ai trouvé que cela fonctionnait mieux pour moi que collectionView.reloadData()parce que cela réduisait l'échange de cellules lorsque des enregistrements étaient insérés.

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
la source
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
la source
1

Juste pour ajouter mon 0,02 $, j'ai essayé les deux versions de la réponse sélectionnée, et la méthode originale fonctionnait mieux pour mes besoins. Je travaille sur une vue de calendrier à défilement infini qui permet à un utilisateur d'entrer dans le calendrier à une semaine donnée, puis de balayer d'avant en arrière et de sélectionner des jours individuels pour filtrer une liste.

Dans ma mise en œuvre, afin de garder les choses performantes sur les appareils plus anciens, le tableau des dates qui représentent la vue du calendrier doit être relativement petit, ce qui signifie contenir environ 5 semaines de dates, avec l'utilisateur au milieu à la 3e semaine. Le problème avec l'utilisation de la deuxième approche est qu'il y a une deuxième étape où vous devez faire défiler la vue de la collection vers le milieu sans animation, ce qui donne une apparence très irrégulière pour une raison quelconque avec l'animation de base bloquée.

Mon code:

[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.
la source
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
la source