Reloading a UICollectionView using reloadData method returns immediately before reloading data
Asked Answered
A

3

26

I need to know when reloading a UICollectionView has completed in order to configure cells afterwards (because I am not the data source for the cells - other wise would have done it already...)

I've tried code such as

[self.collectionView reloadData];
[self configure cells]; // BOOM! cells are nil

I've also tried using

[self.collectionView performBatchUpdates:^{
  [self.collectionView reloadData];
    } completion:^(BOOL finished) {
        // notify that completed and do the configuration now
  }];

but when I reload the data I am getting crashes.

How can I reload the data into the collection, and only when it has finished reloading - do a particular completion handler

Artois answered 14/8, 2013 at 11:48 Comment(2)
I think I have encountered simmilar issue. The problem is that cells are loaded during layoutSubview which takes place during next run loop pass. Unfortunately I didn't find solution yet. I wonder why UICollectionViewDelegate doesn't have methods like: - collectionView:willBeginDisplayingCell:forItemAtIndexPath: while there is: – collectionView:didEndDisplayingCell:forItemAtIndexPath: Did you solve that one already?Crackbrain
What's happening is that the batch updates block expects you to make calls to add and remove row/sections that correspond with the new number of rows and sections that occurs during the reload. If this does not occur, an assertion is thrown.Tryck
C
101

This is caused by cells being added during layoutSubviews not at reloadData. Since layoutSubviews is performed during next run loop pass after reloadData your cells are empty. Try doing this:

[self.collectionView reloadData];
[self.collectionView layoutIfNeeded];
[self configure cells]; 

I had similar issue and resolved it this way.

Crackbrain answered 21/8, 2013 at 11:25 Comment(4)
I had this exact problem where the visibleCells was 0 after calling -reloadData. Calling -layoutIfNeeded fixed this. lukewar's answer should be accepted.Adenoid
I would like to add that this solution is extremely useful in forcing the collection view to reload synchronously, incase you are making async updates from core data, which resolves huge headaches and crashes.Mandatory
You should call setNeedsLayout and then layoutIfNeeded if you want to guarantee a layout cycle.Jampack
but why cells are loading on those cycles, I never had these issues with a lot of UICollectionViews before, until now.Also
B
7

If you'd like to perform some code after your collectionView has completed it's reloadData() method, then try this (Swift):

    self.collectionView.reloadData()
    self.collectionView.layoutIfNeeded()
    dispatch_async(dispatch_get_main_queue()) { () -> Void in
        // Put the code you want to execute when reloadData is complete in here
    }

The reason this works is because the code within the dispatch block gets put to the back of line (also known as a queue). This means that it is waiting in line for all the main thread operations to finish, including reloadData()'s methods, before it becomes it's turn on the main thread.

Bebeeru answered 1/2, 2016 at 21:4 Comment(1)
well, this is not completely true, the dispatch block may be inserted between some main thread operations, see https://mcmap.net/q/55373/-perform-on-next-run-loop-what-39-s-wrong-with-gcd . But your code will work (even without dispatch_async) thanks to layoutIfNeeded (see @lukewar's answer)Bum
C
5

Collection view is not supported to be reloaded animatedly with help of reloadData. All animations must be performed with methods, such as

[collectionView deleteItemsAtIndexPaths:indexesToDelete];
[collectionView insertSections:sectionsToInsert];
[collectionView reloadItemsAtIndexPaths:fooPaths];

inside of performBatchUpdates: block. That reloadData method can only be used for rough refresh, when all items are removed and laid out again without animation.

Caitiff answered 4/6, 2015 at 12:30 Comment(1)
I had a race between pull to refresh and api response and this helped. I simply delete and insert the sections I want to reload instead of reloadData: self.collectionView?.performBatchUpdates({ self.collectionView?.deleteSections(IndexSet(arrayLiteral: 0)) self.collectionView?.insertSections(IndexSet(arrayLiteral: 0)) }, completion: nil)Transship

© 2022 - 2024 — McMap. All rights reserved.