Unikaj animacji UICollectionView po reloadItemsAtIndexPaths


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.


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!


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.


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

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()
    }
}

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

        })
    }
}

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

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

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)
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.