84

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.

rounak
  • 8,669
  • 3
  • 38
  • 58
Nimrod7
  • 1,385
  • 2
  • 16
  • 30

6 Answers6

145

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];
paulmelnikow
  • 16,036
  • 6
  • 56
  • 110
  • 9
    Note from the documentation: You should not call reload data 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. – Raphael Oliveira May 09 '14 at 15:19
  • i think this should be the correct answer. because this worked for me. i have only 1 section and this is working not the correct answer. – Mahesh Agrawal Aug 07 '15 at 05:29
  • Does not work if you're also inserting. Will override sorting animation. – Andres Canella Feb 03 '16 at 17:24
  • 3
    Worth noting that you don't need the `self.collectionView performBatchUpdates` call wrapped around `reloadSections` to get the animation. In fact the `performBatchUpdates` call may cause inadvertant flickering/cell-resize issues in some cases. – Paul Popiel Nov 24 '17 at 02:44
  • 1
    Or just `[self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];` `reloadSections` in collection view or table view will do so with animation. – bauerMusic Apr 15 '18 at 15:25
74

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 
}];
R3D3vil
  • 663
  • 1
  • 7
  • 21
Stripes
  • 4,038
  • 1
  • 22
  • 27
  • 5
    yep, that seems to work, but only if you have save section numbers... else I am receiving "invalid number of sections. The number of sections contained in the collection view after the update (10) must be equal to the number of sections contained in the collection view before the update (16)". I have to manually insert / delete sections ... anyway accepted! Thanks. – Nimrod7 Nov 30 '12 at 07:38
  • 1
    By "manually", do you mean via UICollectionView's insertSections/moveSection:toSection/deleteSections calls? Or something else? (sorry, so far my use of UICollectionView has had a static number of sections) – Stripes Nov 30 '12 at 18:46
  • 1
    yes via those methods... its bit tricky to implement thought, you have to remember the indexpaths before and after the update, the figure what removed inserted, or moved... – Nimrod7 Dec 01 '12 at 15:36
  • 1
    Can we control animation speed in above code (in performBatchUpdates method)? – Swapnil Feb 27 '13 at 05:34
  • 13
    This didn't work for my one-section collection view. I didn't get errors, but it didn't animate either. Substituting `-reloadSections:` makes it work. – paulmelnikow Mar 01 '13 at 19:52
  • 1
    Man, it should be: [self.collectionView performBatchUpdates:^{ [self.collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]]; } completion:nil]; – isanjosgon Oct 30 '14 at 09:24
  • 9
    This answer is not correct, as for my case putting reloadData in performBatchUpdates will crash my app and saying the cell count is not the same before and after. using reloadSections solved my issue. Apple's document also indicates do not call reloadData in animation block – Wingzero Feb 02 '15 at 09:35
  • 1
    The answer is wrong. Watch the last 10 minutes of “Introduction to Collection Views,” WWDC 2018: – James Bush Jun 21 '18 at 02:09
  • @JamesBush, There was no "Introduction to Collection Views" video at WWDC 2018. Couldn't you simply post the solution directly? – Iulian Onofrei Jan 24 '19 at 10:03
  • @JamesBush, Thank you, I didn't know which one were you referring to as the title was wrong, and there is actually a video called "Introducing Collection Views" from 2012. – Iulian Onofrei Jan 25 '19 at 19:43
68

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)
Yariv Nissim
  • 12,599
  • 1
  • 36
  • 44
9

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

Gabriel Wamunyu
  • 543
  • 7
  • 18
3

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];
nemesis
  • 1,340
  • 15
  • 30
  • 1
    Just want to mention a thing which solved crash issue in my case, update the datasource(i.e. section/item count) before calling performUpdates on collection view. – ViruMax Dec 05 '16 at 11:10
  • The animation is from the other operations; there is, and never was UNDER ANY CIRCUMSTANCES an animation for reloadData. insertItems inserts in ascending order, and is performed AFTER updates; deleteItems is the opposite. – James Bush Jun 21 '18 at 02:10
1

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?

Victor Engel
  • 1,872
  • 2
  • 22
  • 43
  • Exactly, it isn’t. reloadData is an animation-less, dual operation; it is first a delete operation, and then an insert operation. – James Bush Jun 21 '18 at 02:14