¿Cómo sé que el UICollectionView se ha cargado completamente?
collection view swift 4 (12)
Tengo que hacer alguna operación cada vez que UICollectionView se haya cargado por completo, es decir, en ese momento se deben llamar todos los métodos de fuente de datos / diseño de UICollectionView. ¿¿Cómo sé eso?? ¿Hay algún método de delegado para conocer el estado cargado de UICollectionView?
Así es como resolví el problema con Swift 3.0:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.collectionView.visibleCells.isEmpty {
// stuff
}
}
Como respondió dezinezync , lo que necesita es enviar a la cola principal un bloque de código después de reloadData
desde un UITableView
o UICollectionView
, y luego este bloque se ejecutará después de que las celdas se reloadData
de la cola.
Para hacer esto más directo al usarlo, usaría una extensión como esta:
extension UICollectionView {
func reloadData(_ completion: @escaping () -> Void) {
reloadData()
DispatchQueue.main.async { completion() }
}
}
También se puede implementar en un UITableView
también
Def hacer 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
}
}
Entonces llama así dentro de tu CV
let collection = MyCollectionView()
self.collection.reloadData(completion: {
})
Asegúrese de que está utilizando la subclase!
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 la interfaz de usuario) aloja la cola principal, que es de naturaleza serial, es decir, funciona de la 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 está bloqueando también. Entonces, si está reloadData
toma 3s para ejecutarse, el procesamiento del segundo bloque será diferido por esos 3s.
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
})
Esto funciona para mí:
__weak typeof(self) wself= self;
[self.contentCollectionView performBatchUpdates:^{
[wself.contentCollectionView reloadData];
} completion:^(BOOL finished) {
[wself pageViewCurrentIndexDidChanged:self.contentCollectionView];
}];
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
// e.g. scrollToItemAtIndexPath
let newIndexPath = NSIndexPath(forItem: 1, inSection: 0)
strongSelf.collectionView.scrollToItemAtIndexPath(newIndexPath, atScrollPosition: UICollectionViewScrollPosition.Left, animated: false)
})
Necesitaba que se realizara alguna acción en todas las celdas visibles cuando la vista de colección se carga antes de que sea visible para el usuario, usé:
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 esto se activará cuando se desplace por la vista de colección, por lo que para evitar esta sobrecarga, agregué:
private var souldPerformAction: Bool = true
y en la propia acción:
private func modifyVisibleCells() {
if self.shouldPerformAction {
// perform action
...
...
}
self.shouldPerformAction = false
}
La acción se seguirá realizando 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 la bandera booleana evitará que se ejecute nuevamente después de que el usuario comience a interactuar con la vista de colección.
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;
}
Puedes hacer así ...
- (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.
}
Sólo para agregar a una gran respuesta @dezinezync:
Swift 3+
collectionView.collectionViewLayout.invalidateLayout() // or reloadData()
DispatchQueue.main.async {
// your stuff here executing after collectionView has been layouted
}
// 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];
}