¿Cómo sé que UICollectionView se ha cargado por completo?

Tengo que hacer alguna operación cada vez que UICollectionView se haya cargado por completo, es decir, en ese momento se deben llamar a todos los métodos de fuente de datos / diseño de UICollectionView. ¿¿Cómo sé eso?? ¿Hay algún método delegado para conocer el estado cargado de UICollectionView?

// 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]; } 

Esto funcionó para mí:

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

Sintaxis de Swift 4:

 self.collectionView.reloadData() self.collectionView.performBatchUpdates(nil, completion: { (result) in // ready }) 

En realidad es bastante simple.

Cuando, por ejemplo, llama al método reloadData de UICollectionView o al método invalidateLayout de su diseño, haga lo siguiente:

 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 }); 

Por qué esto funciona:

El hilo principal (que es donde deberíamos hacer todas las actualizaciones de UI) alberga la cola principal, que es de naturaleza serial, es decir, funciona de manera FIFO. Entonces, en el ejemplo anterior, se llama al primer bloque, que tiene reloadData nuestro método reloadData , seguido de cualquier otra cosa en el segundo bloque.

Ahora el hilo principal también está bloqueando. Entonces, si reloadData tarda 3s en ejecutarse, el procesamiento del segundo bloque será aplazado por esos 3s.

Solo para agregar a una gran respuesta de @dezinezync:

Swift 3+

 collectionView.collectionViewLayout.invalidateLayout() // or reloadData() DispatchQueue.main.async { // your stuff here executing after collectionView has been layouted } 

Hazlo asi:

  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) }) 

Esto funciona para mí:

 __weak typeof(self) wself= self; [self.contentCollectionView performBatchUpdates:^{ [wself.contentCollectionView reloadData]; } completion:^(BOOL finished) { [wself pageViewCurrentIndexDidChanged:self.contentCollectionView]; }]; 

Así es como resolví el problema con Swift 3.0:

 override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if !self.collectionView.visibleCells.isEmpty { // stuff } } 

Def haz esto:

 //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 } } 

Luego llame así dentro de su VC

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

¡Asegúrate de estar usando la subclase!

Necesitaba hacer alguna acción en todas las celdas visibles cuando la vista de colección se carga antes de que sea visible para el usuario, utilicé:

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

Preste atención a que se invocará esto al desplazarse por la vista de recostackción, por lo que para evitar esta sobrecarga, agregué:

 private var souldPerformAction: Bool = true 

y en la acción en sí:

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

La acción todavía se realizará varias veces, como el número de celdas visibles en el estado inicial. pero en todas esas llamadas, tendrá el mismo número de celdas visibles (todas ellas). Y el indicador booleano evitará que se vuelva a ejecutar después de que el usuario comenzó a interactuar con la vista de colección.

Puedes hacer esto …

  - (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. } 

Prueba esto:

 - (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; }