Unikaj animacji UICollectionView po reloadItemsAtIndexPaths

91

UICollectionView animuje elementy po wywołaniu reloadItemsAtIndexPaths (animacja zanikania).

Czy jest sposób, aby uniknąć tej animacji?

iOS 6

Marcin
źródło

Odpowiedzi:

231

Warto zauważyć, że jeśli celujesz w system iOS 7 i nowszy, możesz użyć nowej UIViewmetody performWithoutAnimation:. Podejrzewam, że pod maską działa to podobnie jak inne odpowiedzi tutaj (tymczasowo wyłączamUIView animacji / akcji Core Animation), ale składnia jest ładna i przejrzysta.

A więc w szczególności w przypadku tego pytania ...

Cel C:

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


Szybki:

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


Oczywiście tę zasadę można zastosować w każdej sytuacji, w której chcesz mieć pewność, że zmiana nie jest animowana.

Stuart
źródło
3
To zadziałało lepiej niż zaakceptowana odpowiedź dla mnie na iOS 7+.
Philippe Sabourin
Wyłącza wszystkie animacje nie tylko dla widoku kolekcji
user2159978
10
@ user2159978 Zgadza się; wszystkie odpowiedzi tutaj tymczasowo wyłącz animacje UIView. Animacje są wyłączone tylko dla akcji zainicjowanych z przekazanego bloku, w tym przypadku tylko dla ponownego załadowania widoku kolekcji. Jest to całkowicie poprawna odpowiedź na zadane pytanie, więc nie sądzę, aby zasługiwała na negatywny głos.
Stuart
Niesamowite rozwiązanie. Używanie tego zamiast animateWithDuration: 0 zapobiega szybkiej, ale widocznej usterce układu podczas korzystania z samoczynnie zmieniających się rozmiarów komórek widoku kolekcji bez elementu itemSize
Ethan Gill
1
To rozwiązanie już nie działa na iOS 14, w tym bloku używam reloadData, ale z iOS 13 zadziałało
Maray97
161

Możesz także spróbować tego:

UICollectionView *collectionView;

...

[UIView setAnimationsEnabled:NO];

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

Edytować:

Odkryłem również, że jeśli performBatchUpdateszawiniesz blok animacji UIView, zamiast domyślnej animacji zostanie użyta animacja UIView, więc możesz po prostu ustawić czas trwania animacji na 0, na przykład:

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

Jest to super fajne, jeśli chcesz używać sprężystych animacji iOS 7 podczas wstawiania i usuwania!

Sam
źródło
1
Dziękuję Ci. Było to bardzo pomocne w dodawaniu stoperów do komórek uicollectionview.
hatunike
Znakomity! Użyłem tego z insertCells i przy włączonej klawiaturze, aby animować collectionView do nowego przesunięcia po wstawieniu komórki.
David H
5
Jak mówi Peter, koliduje to z innymi animacjami. Zamiast tego powinieneś wywołać [UIView setAnimationsEnabled: YES] poza blokiem uzupełniania. W ten sposób zapobiegniesz tylko tej 1 animacji.
Adlai Holler
2
Dodałem alternatywną metodę, która robi dokładnie to samo.
Sam
1
Owijanie w performBatchUpdatesśrodku animateWithDurationjest genialne! Dzięki za wskazówkę!
Robert,
19

UICollectionView animuje elementy po wywołaniu reloadItemsAtIndexPaths (animacja zanikania).

Czy jest sposób, aby uniknąć tej animacji?

iOS 6

Zakładam, że używasz FlowLayout. Ponieważ próbujesz pozbyć się animacji zanikania, spróbuj tego:

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
    }

}

To bardzo stare pytanie, więc prawdopodobnie nie masz już na celu iOS 6. Osobiście pracowałem nad tvOS 11 i miałem to samo pytanie, więc to jest tutaj dla każdego, kto ma ten sam problem.

Matt Mc
źródło
1
Pojawiły się 3 lub 4 komentarze dotyczące tej odpowiedzi, które brzmiały: „Wow, to działa naprawdę dobrze!” Ktoś zdecydował się usunąć komentarze. Myślę, że w rzeczywistości jest to nowoczesny i niehackowy sposób osiągnięcia tego celu, a te komentarze były tego uznaniem.
Matt Mc
8

Napisałem kategorię na UICollectionView, aby to zrobić. Sztuczka polega na tym, aby wyłączyć wszystkie animacje podczas przeładowywania:

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

[self reloadItemsAtIndexPaths:indexPaths];

if (!animated) {
    [CATransaction commit];
}
Giorgio Calderolla
źródło
Otrzymuję również odpowiedź od Apple, że nie powinno to powodować żadnej animacji, jeśli tak, to błąd. Nie wiem, czy robię coś źle, czy to błąd.
Marcin
2
Możesz też zrobić to CATransaction.setDisableActions(true)jako skrót.
CIFilter
5
extension UICollectionView {
    func reloadWithoutAnimation(){
        CATransaction.begin()
        CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
        self.reloadData()
        CATransaction.commit()
    }
}
Amjad Tubasi
źródło
to jest szybka 3 składnia
Amjad Tubasi
to działało dobrze, nawet lepiej niż UIView.performWithoutAnimation
Lance Samaria
5

Oto wersja Swift 3 performBatchUpdatesbez animacji do pliku UICollectionView. Okazało się, że działa to lepiej dla mnie niż collectionView.reloadData()dlatego, że ogranicza wymianę komórek po wstawieniu rekordów.

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
źródło
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
źródło
1

Aby dodać 0,02 $, wypróbowałem obie wersje wybranej odpowiedzi i oryginalny sposób działał lepiej dla moich celów. Pracuję nad nieskończonym przewijanym widokiem kalendarza, który pozwala użytkownikowi wejść do kalendarza w danym tygodniu, a następnie przesuwać palcem w przód iw tył i wybierać poszczególne dni do filtrowania listy.

W mojej implementacji, aby zachować wydajność na starszych urządzeniach, tablica dat reprezentująca widok kalendarza musi być stosunkowo mała, co oznacza przechowywanie dat o wartości około 5 tygodni, z użytkownikiem w środku w trzecim tygodniu. Problem ze stosowaniem drugiego podejścia polega na tym, że jest drugi krok, w którym musisz przewinąć widok kolekcji z powrotem do środka bez animacji, co z jakiegoś powodu powoduje bardzo postrzępiony wygląd z zablokowaną animacją podstawową.

Mój kod:

[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.
źródło
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
źródło