Evita l'animazione di UICollectionView dopo reloadItemsAtIndexPaths


92

UICollectionView anima gli elementi dopo che è stato chiamato reloadItemsAtIndexPaths (fade animation).

C'è un modo per evitare questa animazione?

iOS 6

Risposte:


234

Vale la pena notare che se stai prendendo di mira iOS 7 e versioni successive, puoi utilizzare il nuovo UIViewmetodo performWithoutAnimation:. Sospetto che sotto il cofano questo stia facendo più o meno lo stesso delle altre risposte qui (disabilitando temporaneamente le UIViewanimazioni / le azioni di animazione principale), ma la sintassi è bella e pulita.

Quindi per questa domanda in particolare ...

Obiettivo-C:

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


Swift:

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


Naturalmente questo principio può essere applicato a qualsiasi situazione in cui si desidera garantire che un cambiamento non sia animato.


3
Questo ha funzionato meglio della risposta accettata per me su iOS 7+.
Philippe Sabourin

Disabilita tutte le animazioni non solo per la visualizzazione della raccolta
user2159978

10
@ user2159978 Corretto; tutte le risposte qui disabilitano temporaneamente le animazioni di UIView. Le animazioni sono disabilitate solo per le azioni avviate dall'interno del blocco passato, in questo caso solo il ricaricamento della vista raccolta. È una risposta perfettamente valida alla domanda che viene posta, quindi non credo che meriti un voto negativo.
Stuart

Soluzione incredibile. L'utilizzo di questo invece di animateWithDuration: 0 impedisce un glitch di layout rapido ma visibile quando si utilizzano celle di visualizzazione della raccolta a ridimensionamento automatico senza itemSize
Ethan Gill

1
Questa soluzione non funziona più su iOS 14, all'interno di questo blocco utilizzo reloadData, ma con iOS 13 ha funzionato
Maray97

161

Potresti anche provare questo:

UICollectionView *collectionView;

...

[UIView setAnimationsEnabled:NO];

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

Modificare:

Ho anche scoperto che se performBatchUpdatesesegui il wrapping in un blocco di animazione UIView, l'animazione UIView viene utilizzata al posto dell'animazione predefinita, quindi puoi semplicemente impostare la durata dell'animazione su 0, in questo modo:

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

Questo è molto interessante se desideri utilizzare le animazioni elastiche di iOS 7 durante gli inserimenti e le eliminazioni!


1
Grazie. Ciò è stato molto utile per aggiungere i timer del cronometro alle celle uicollectionview.
hatunike

Brillante! L'ho usato con insertCells e con la tastiera in alto per animare collectionView con un nuovo offset dopo che la cella è stata inserita.
David H,

5
Come dice Peter, questo interferisce con altre animazioni. Invece, dovresti chiamare [UIView setAnimationsEnabled: YES] al di fuori del blocco di completamento. In questo modo previeni solo quell'animazione.
Adlai Holler

2
Ho aggiunto un metodo alternativo, che fa esattamente la stessa cosa.
Sam

1
Avvolgere performBatchUpdatesall'interno animateWithDurationè geniale! Grazie per il consiglio!
Robert

20

UICollectionView anima gli elementi dopo che è stato chiamato reloadItemsAtIndexPaths (fade animation).

C'è un modo per evitare questa animazione?

iOS 6

Presumo tu stia usando un FlowLayout. Dato che stai cercando di sbarazzarti dell'animazione in dissolvenza, prova questo:

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
    }

}

Questa è una domanda molto vecchia, quindi probabilmente non stai più prendendo di mira iOS 6. Stavo lavorando personalmente su tvOS 11 e avevo la stessa domanda, quindi questo è qui per chiunque abbia lo stesso problema.


1
C'erano 3 o 4 commenti su questa risposta all'effetto di "Wow, funziona davvero bene!" Qualcuno ha deciso di rimuovere i commenti. Penso, in effetti, che questo sia il modo moderno e non hacker di ottenere questo risultato, e quei commenti ne erano un riconoscimento.
Matt Mc

8

Ho scritto una categoria su UICollectionView per fare proprio questo. Il trucco è disabilitare tutte le animazioni durante il caricamento:

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

[self reloadItemsAtIndexPaths:indexPaths];

if (!animated) {
    [CATransaction commit];
}

Ricevo anche la risposta da Apple che questo non dovrebbe fare alcuna animazione, in tal caso è un bug. Non so se sto facendo qualcosa di sbagliato o è un bug.
Marcin

2
Puoi anche fare solo CATransaction.setDisableActions(true)una scorciatoia per questo.
Filtro CIF

5
extension UICollectionView {
    func reloadWithoutAnimation(){
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        self.reloadData()
        CATransaction.commit()
    }
}

questa è la sintassi rapida 3
Amjad Tubasi

ha funzionato bene, anche meglio di UIView.performWithoutAnimation
Lance Samaria,

5

Ecco una versione di Swift 3 performBatchUpdatessenza animazione in un file UICollectionView. Ho trovato che questo funzioni meglio per me piuttosto che collectionView.reloadData()perché riduceva lo scambio di celle quando venivano inseriti i record.

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

        })
    }
}

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];
    }

}

1

Solo per aggiungere i miei $ 0,02, ho provato entrambe le versioni della risposta selezionata e il modo originale ha funzionato meglio per i miei scopi. Sto lavorando su una visualizzazione del calendario a scorrimento infinito che consente a un utente di accedere al calendario in una determinata settimana, quindi scorrere avanti e indietro e selezionare i singoli giorni per filtrare un elenco.

Nella mia implementazione, per mantenere le cose performanti sui dispositivi più vecchi, la serie di date che rappresentano la visualizzazione del calendario deve essere mantenuta relativamente piccola, il che significa contenere circa 5 settimane di date, con l'utente al centro alla terza settimana. Il problema con l'utilizzo del secondo approccio è che c'è un secondo passaggio in cui è necessario scorrere la vista della raccolta fino al centro senza un'animazione, il che rende per qualche motivo un aspetto molto frastagliato con l'animazione di base bloccata.

Il mio codice:

[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];

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)
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.