UICollectionView animate data change
IosUicollectionviewIos Problem Overview
In my Project I use UICollectionView to display a grid of icons.
The user is able to change the ordering by clicking a segmented control which calling a fetch from core data with different NSSortDescriptor.
The amount of data is always the same, just ending up in different sections / rows:
- (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];
}
The problem is that reloadData doesn't animate the change, UICollectionView just pops with the new data.
Should I keep track in which indexPath a cell was before and after change, and use [self.collectionView moveItemAtIndexPath: toIndexPath:] to perform the animation for the change or there is a better method ?
I didn't get much into subclassing collectionViews so any help will be great...
Thanks, Bill.
Ios Solutions
Solution 1 - Ios
Wrapping -reloadData
in -performBatchUpdates:
does not seem to cause a one-section collection view to animate.
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadData];
} completion:nil];
However, this code works:
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];
Solution 2 - Ios
reloadData doesn't animate, nor does it reliabably do so when put in a UIView animation block. It wants to be in a UICollecitonView performBatchUpdates block, so try something more like:
[self.collectionView performBatchUpdates:^{
[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
} completion:^(BOOL finished) {
// do something on completion
}];
Solution 3 - Ios
This is what I did to animate reload of ALL SECTIONS:
[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)
Solution 4 - Ios
For Swift users, if your collectionview only has one section:
self.collectionView.performBatchUpdates({
let indexSet = IndexSet(integersIn: 0...0)
self.collectionView.reloadSections(indexSet)
}, completion: nil)
As seen on https://stackoverflow.com/a/42001389/4455570
Solution 5 - Ios
Reloading the whole collection view inside a performBatchUpdates:completion:
block does a glitchy animation for me on iOS 9 simulator. If you have a specific UICollectionViewCell
you want do delete, or if you have it's index path, you could call deleteItemsAtIndexPaths:
in that block. By using deleteItemsAtIndexPaths:
, it does a smooth and nice animation.
UICollectionViewCell* cellToDelete = /* ... */;
NSIndexPath* indexPathToDelete = /* ... */;
[self.collectionView performBatchUpdates:^{
[self.collectionView deleteItemsAtIndexPaths:@[[self.collectionView indexPathForCell:cell]]];
// or...
[self.collectionView deleteItemsAtIndexPaths:@[indexPath]];
} completion:nil];
Solution 6 - Ios
The help text says:
> Call this method to reload all of the items in the collection view. > This causes the collection view to discard any currently visible items > and redisplay them. For efficiency, the collection view only displays > those cells and supplementary views that are visible. If the > collection data shrinks as a result of the reload, the collection view > adjusts its scrolling offsets accordingly. You should not call this > method in the middle of animation blocks where items are being > inserted or deleted. Insertions and deletions automatically cause the > table’s data to be updated appropriately.
I think the key part is "causes the collection view to discard any currently visible items". How is it going to animate the movement of items it has discarded?