UICollectionView animiert Datenänderungen

83

In meinem Projekt verwende ich UICollectionView, um ein Raster von Symbolen anzuzeigen.

Der Benutzer kann die Reihenfolge ändern, indem er auf ein segmentiertes Steuerelement klickt, das einen Abruf von Kerndaten mit einem anderen NSSortDescriptor aufruft.

Die Datenmenge ist immer gleich und endet nur in verschiedenen Abschnitten / Zeilen:

- (IBAction)sortSegmentedControlChanged:(id)sender {

   _fetchedResultsController = nil;
   _fetchedResultsController = [self newFetchResultsControllerForSort];

   NSError *error;
   if (![self.fetchedResultsController performFetch:&error]) {
       NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
   }

   [self.collectionView reloadData];
}

Das Problem ist, dass reloadData die Änderung nicht animiert, UICollectionView nur mit den neuen Daten angezeigt wird.

Sollte ich verfolgen, in welchem ​​Indexpfad sich eine Zelle vor und nach der Änderung befand, und [self.collectionView moveItemAtIndexPath: toIndexPath:] verwenden, um die Animation für die Änderung auszuführen, oder gibt es eine bessere Methode?

Ich habe mich nicht viel mit dem Unterklassen von collectionViews beschäftigt, daher ist jede Hilfe großartig ...

Danke, Bill.

Nimrod7
quelle
Haben Sie versucht, diesen reloadDataAufruf in einen Animationsblock zu packen?
Simon

Antworten:

70

reloadData animiert nicht und tut dies auch nicht zuverlässig, wenn es in einen UIView-Animationsblock eingefügt wird. Es möchte sich in einem UICollecitonView performBatchUpdates-Block befinden. Versuchen Sie also etwas wie:

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:^(BOOL finished) {
    // do something on completion 
}];
Streifen
quelle
5
Ja, das scheint zu funktionieren, aber nur, wenn Sie Abschnittsnummern gespeichert haben ... sonst erhalte ich eine "ungültige Anzahl von Abschnitten. Die Anzahl der Abschnitte, die in der Sammlungsansicht nach dem Update (10) enthalten sind, muss gleich der Anzahl von sein Abschnitte, die vor dem Update in der Sammlungsansicht enthalten waren (16) ". Ich muss Abschnitte manuell einfügen / löschen ... trotzdem akzeptiert! Vielen Dank.
Nimrod7
1
Mit "manuell" meinen Sie über die Aufrufe insertSections / moveSection von UICollectionView: toSection / deleteSections? Oder etwas anderes? (Entschuldigung, bisher hatte meine Verwendung von UICollectionView eine statische Anzahl von Abschnitten)
Stripes
1
Ja, über diese Methoden ... es ist etwas schwierig, Gedanken zu implementieren. Sie müssen sich die Indexpfade vor und nach dem Update merken, die Zahl, die entfernt oder eingefügt wurde ...
Nimrod7
12
Dies funktionierte für meine Sammlungsansicht mit einem Abschnitt nicht. Ich habe keine Fehler bekommen, aber es hat auch nicht animiert. Durch Ersetzen -reloadSections:funktioniert es.
Paulmelnikow
9
Diese Antwort ist nicht korrekt, da in meinem Fall das Einfügen von reloadData in performBatchUpdates meine App zum Absturz bringt und die Anzahl der Zellen vorher und nachher nicht gleich ist. Die Verwendung von reloadSections hat mein Problem gelöst. Apples Dokument zeigt auch an, dass reloadData im Animationsblock nicht aufgerufen wird
Wingzero
145

Wrapping -reloadDatain -performBatchUpdates:scheint nicht zu einem Ein-Abschnitt Sammlung Blick auf belebter zu verursachen.

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadData];
} completion:nil];

Dieser Code funktioniert jedoch:

[self.collectionView performBatchUpdates:^{
    [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];
paulmelnikow
quelle
9
Hinweis aus der Dokumentation: Sie sollten die Methode zum erneuten Laden von Daten nicht in der Mitte von Animationsblöcken aufrufen, in denen Elemente eingefügt oder gelöscht werden. Durch Einfügungen und Löschungen werden die Daten der Tabelle automatisch entsprechend aktualisiert.
Raphael Oliveira
Ich denke, das sollte die richtige Antwort sein. weil das bei mir funktioniert hat. Ich habe nur 1 Abschnitt und dies funktioniert nicht die richtige Antwort.
Mahesh Agrawal
Funktioniert nicht, wenn Sie auch einfügen. Überschreibt die Sortieranimation.
Andres Canella
2
Beachten Sie, dass Sie den self.collectionView performBatchUpdatesAnruf nicht benötigen reloadSections, um die Animation zu erhalten. Tatsächlich kann der performBatchUpdatesAnruf in einigen Fällen zu versehentlichem Flackern / zur Größenänderung von Zellen führen.
Paul Popiel
Oder nur [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]]; reloadSectionsin der Sammlungsansicht oder Tabellenansicht mit Animation.
BauerMusic
67

Dies ist, was ich getan habe, um das Nachladen ALLER ABSCHNITTE zu animieren:

[self.collectionView reloadSections:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, self.collectionView.numberOfSections)]];

Swift 3

let range = Range(uncheckedBounds: (0, collectionView.numberOfSections))
let indexSet = IndexSet(integersIn: range)
collectionView.reloadSections(indexSet)
Yariv Nissim
quelle
8

Für Swift-Benutzer, wenn Ihre Sammlungsansicht nur einen Abschnitt enthält:

self.collectionView.performBatchUpdates({
                    let indexSet = IndexSet(integersIn: 0...0)
                    self.collectionView.reloadSections(indexSet)
                }, completion: nil)

Wie unter https://stackoverflow.com/a/42001389/4455570 zu sehen

Gabriel Wamunyu
quelle
2

Das Neuladen der gesamten Sammlungsansicht innerhalb eines performBatchUpdates:completion:Blocks führt für mich auf dem iOS 9-Simulator zu einer fehlerhaften Animation. Wenn Sie ein bestimmtes UICollectionViewCellElement haben, das Sie löschen möchten, oder wenn Sie dessen Indexpfad haben, können Sie deleteItemsAtIndexPaths:diesen Block aufrufen . Durch die Verwendung deleteItemsAtIndexPaths:wird eine reibungslose und schöne Animation erstellt.

UICollectionViewCell* cellToDelete = /* ... */;
NSIndexPath* indexPathToDelete = /* ... */;

[self.collectionView performBatchUpdates:^{
    [self.collectionView deleteItemsAtIndexPaths:@[[self.collectionView indexPathForCell:cell]]];
    // or...
    [self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:nil];
Nemesis
quelle
1
Ich möchte nur eine Sache erwähnen, die das Absturzproblem in meinem Fall gelöst hat. Aktualisieren Sie die Datenquelle (dh die Anzahl der Abschnitte / Elemente), bevor Sie performUpdates in der Sammlungsansicht aufrufen.
ViruMax
Die Animation stammt aus den anderen Operationen. Es gibt und gab unter KEINEN UMSTÄNDEN eine Animation für reloadData. insertItems wird in aufsteigender Reihenfolge eingefügt und nach Aktualisierungen ausgeführt. deleteItems ist das Gegenteil.
James Bush
1

Der Hilfetext sagt:

Rufen Sie diese Methode auf, um alle Elemente in der Sammlungsansicht neu zu laden. Dadurch werden in der Sammlungsansicht alle derzeit sichtbaren Elemente verworfen und erneut angezeigt. Aus Effizienzgründen werden in der Sammlungsansicht nur die Zellen und Zusatzansichten angezeigt, die sichtbar sind. Wenn die Erfassungsdaten infolge des erneuten Ladens kleiner werden, passt die Erfassungsansicht ihre Bildlaufversätze entsprechend an. Sie sollten diese Methode nicht in der Mitte von Animationsblöcken aufrufen, in denen Elemente eingefügt oder gelöscht werden. Durch Einfügungen und Löschungen werden die Daten der Tabelle automatisch entsprechend aktualisiert.

Ich denke, der Schlüsselteil ist "bewirkt, dass die Sammlungsansicht alle derzeit sichtbaren Elemente verwirft". Wie wird es die Bewegung von Gegenständen animieren, die es weggeworfen hat?

Victor Engel
quelle
Genau ist es nicht. reloadData ist eine animationslose Doppeloperation. Es ist zuerst eine Löschoperation und dann eine Einfügeoperation.
James Bush