How to find out that UICollectionView is fully loaded?

I need to perform some operation whenever the UICollectionView has been fully loaded, i.e. at this time, all calls should be called by all methods of the UatollectionView datasource / layout. How do I know that? Is there any delegate method to find out the loading status of a UICollectionView?

+69
reload delegates uicollectionview
Dec 24 '12 at 10:23
source share
14 answers
// In viewDidLoad [self.collectionView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionOld context:NULL]; - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { // You will get here when the reloadData finished } - (void)dealloc { [self.collectionView removeObserver:self forKeyPath:@"contentSize" context:NULL]; } 
+52
Oct 24 '14 at 2:02
source share
— -

This worked for me:

 [self.collectionView reloadData]; [self.collectionView performBatchUpdates:^{} completion:^(BOOL finished) { /// collection-view finished reload }]; 

Swift 4 Syntax:

 self.collectionView.reloadData() self.collectionView.performBatchUpdates(nil, completion: { (result) in // ready }) 
+106
Jun 05 '15 at 14:02
source share

It is actually quite simple.

If, for example, you call the reloadData UICollectionView method or its invalidateLayout layout, you do the following:

 dispatch_async(dispatch_get_main_queue(), ^{ [self.collectionView reloadData]; }); dispatch_async(dispatch_get_main_queue(), ^{ //your stuff happens here //after the reloadData/invalidateLayout finishes executing }); 

Why does it work:

The main thread (in which we should do all the UI updates) contains the main queue, which is sequential, i.e. works in FIFO mode. Thus, in the above example, the first block is called, in which our reloadData method is reloadData , followed by something else in the second block.

Now the main thread is also blocked. So if you reloadData take 3 seconds to execute, processing the second block will be delayed by these 3s.

+24
Dec 19 '13 at 13:39 on
source share

Just add the excellent @dezinezync answer:

Swift 3+

 collectionView.collectionViewLayout.invalidateLayout() // or reloadData() DispatchQueue.main.async { // your stuff here executing after collectionView has been layouted } 
+9
Sep 22 '17 at 18:43 on
source share

Do it like this:

  UIView.animateWithDuration(0.0, animations: { [weak self] in guard let strongSelf = self else { return } strongSelf.collectionView.reloadData() }, completion: { [weak self] (finished) in guard let strongSelf = self else { return } // Do whatever is needed, reload is finished here // eg scrollToItemAtIndexPath let newIndexPath = NSIndexPath(forItem: 1, inSection: 0) strongSelf.collectionView.scrollToItemAtIndexPath(newIndexPath, atScrollPosition: UICollectionViewScrollPosition.Left, animated: false) }) 
+4
Sep 10 '16 at 11:03
source share

This works for me:

 __weak typeof(self) wself= self; [self.contentCollectionView performBatchUpdates:^{ [wself.contentCollectionView reloadData]; } completion:^(BOOL finished) { [wself pageViewCurrentIndexDidChanged:self.contentCollectionView]; }]; 
+1
Jan 12 '16 at 3:18
source share

As dezinezync replied, you need to send a block of code to the main queue after reloadData from a UITableView or UICollectionView , and then this block will be executed after removing cells from the queue.

To make it easier to use, I would use an extension like this:

 extension UICollectionView { func reloadData(_ completion: @escaping () -> Void) { reloadData() DispatchQueue.main.async { completion() } } } 

It can also be implemented in a UITableView

+1
Nov 02
source share

Def do this:

 //Subclass UICollectionView class MyCollectionView: UICollectionView { //Store a completion block as a property var completion: (() -> Void)? //Make a custom funciton to reload data with a completion handle func reloadData(completion: @escaping() -> Void) { //Set the completion handle to the stored property self.completion = completion //Call super super.reloadData() } //Override layoutSubviews override func layoutSubviews() { //Call super super.layoutSubviews() //Call the completion self.completion?() //Set the completion to nil so it is reset and doesn't keep gettign called self.completion = nil } } 

Then call like this inside your VC

 let collection = MyCollectionView() self.collection.reloadData(completion: { }) 

Make sure you use a subclass!

0
Jan 17 '18 at 23:02
source share

I needed to perform some actions with all visible cells, when the collection view is loaded before it becomes visible to the user, I used:

 public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { if shouldPerformBatch { self.collectionView.performBatchUpdates(nil) { completed in self.modifyVisibleCells() } } } 

Note that this will be called when scrolling through the collection view, so to prevent this, I added:

 private var souldPerformAction: Bool = true 

and in the action itself:

 private func modifyVisibleCells() { if self.shouldPerformAction { // perform action ... ... } self.shouldPerformAction = false } 

The action will continue to be performed several times, since the number of visible cells in the initial state. but in all these calls you will have the same number of visible cells (all of them). A logical flag will prevent it from re-launching after the user begins to interact with the collection view.

0
May 6 '18 at 13:15
source share

Another approach using RxSwift / RxCocoa:

  collectionView.rx.observe(CGSize.self, "contentSize") .subscribe(onNext: { size in print(size as Any) }) .disposed(by: disposeBag) 
0
Jan 02 '19 at 9:28
source share

This work for me:

 - (void)viewDidLoad { [super viewDidLoad]; int ScrollToIndex = 4; [self.UICollectionView performBatchUpdates:^{} completion:^(BOOL finished) { NSIndexPath *indexPath = [NSIndexPath indexPathForItem:ScrollToIndex inSection:0]; [self.UICollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO]; }]; } 
0
Jan 23 '19 at 8:16
source share

This is how I solved the problem with Swift 3.0:

 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if !self.collectionView.visibleCells.isEmpty { // stuff } } 
-one
Feb 16 '17 at 10:04 on
source share

You can do like this:

  - (void)reloadMyCollectionView{ [myCollectionView reload]; [self performSelector:@selector(myStuff) withObject:nil afterDelay:0.0]; } - (void)myStuff{ // Do your stuff here. This will method will get called once your collection view get loaded. } 
-9
Apr 09 '13 at 7:19
source share

Try the following:

 - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return _Items.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { UICollectionViewCell *cell; //Some cell stuff here... if(indexPath.row == _Items.count-1){ //THIS IS THE LAST CELL, SO TABLE IS LOADED! DO STUFF! } return cell; } 
-9
Nov 07 '13 at 4:43
source share



All Articles