J'essaie de faire défiler vers le bas d'un UITableView après avoir terminé [self.tableView reloadData]
J'avais à l'origine
[self.tableView reloadData]
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Mais ensuite, j'ai lu que reloadData est asynchrone, donc le défilement ne se produit pas depuis le self.tableView
, [self.tableView numberOfSections]
et [self.tableView numberOfRowsinSection
sont tous à 0.
Merci!
Ce qui est bizarre, c'est que j'utilise:
[self.tableView reloadData];
NSLog(@"Number of Sections %d", [self.tableView numberOfSections]);
NSLog(@"Number of Rows %d", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);
Dans la console, il renvoie Sections = 1, Row = -1;
Lorsque je fais exactement les mêmes NSLogs, cellForRowAtIndexPath
j'obtiens Sections = 1 et Row = 8; (8 a raison)
Réponses:
Le rechargement se produit lors de la prochaine passe de mise en page, ce qui se produit normalement lorsque vous retournez le contrôle à la boucle d'exécution (après, par exemple, l'action de votre bouton ou tout ce qui retourne).
Donc, une façon d'exécuter quelque chose après le rechargement de la vue de table est simplement de forcer la vue de table à effectuer la mise en page immédiatement:
Une autre façon consiste à planifier votre code de mise en page pour qu'il s'exécute plus tard en utilisant
dispatch_async
:METTRE À JOUR
Après une enquête plus approfondie, je constate que la vue de la table envoie
tableView:numberOfSections:
ettableView:numberOfRowsInSection:
à sa source de données avant de revenirreloadData
. Si le délégué implémentetableView:heightForRowAtIndexPath:
, la vue de table envoie également cela (pour chaque ligne) avant de revenirreloadData
.Cependant, la vue de table n'envoie pas
tableView:cellForRowAtIndexPath:
outableView:headerViewForSection
jusqu'à la phase de disposition, ce qui se produit par défaut lorsque vous retournez le contrôle à la boucle d'exécution.Je trouve également que dans un petit programme de test, le code de votre question défile correctement vers le bas de la vue du tableau, sans que je fasse rien de spécial (comme envoyer
layoutIfNeeded
ou utiliserdispatch_async
).la source
dispatch_async(dispatch_get_main_queue())
méthode n'est pas garantie de fonctionner. Je vois un comportement non déterministe avec lui, dans lequel parfois le système a terminé les layoutSubviews et le rendu de la cellule avant le bloc d'achèvement, et parfois après. Je publierai une réponse qui a fonctionné pour moi ci-dessous.dispatch_async(dispatch_get_main_queue())
ne pas toujours travailler. Voir des résultats aléatoires ici.NSRunLoop
. Une boucle d'exécution comporte différentes phases et vous pouvez planifier un rappel pour une phase spécifique (en utilisant aCFRunLoopObserver
). UIKit planifie la mise en page lors d'une phase ultérieure, après le retour de votre gestionnaire d'événements.Rapide:
Objectif c:
la source
tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
méthode et en insérant dans mon override ce que je voulais pour informer que le rechargement était terminé.À partir de Xcode 8.2.1, iOS 10 et swift 3,
Vous pouvez déterminer la fin de
tableView.reloadData()
facilement en utilisant un bloc CATransaction:Ce qui précède fonctionne également pour déterminer la fin de reloadData () d'UICollectionView et reloadAllComponents () d'UIPickerView.
la source
beginUpdates
et dans lesendUpdates
appels.setCompletionBlock
mesnumberOfSections
spectacles 2 ... jusqu'ici tout va bien. Pourtant si à l'intérieursetCompletionBlock
je le faistableView.headerView(forSection: 1)
ça revientnil
!!! par conséquent, je pense que ce bloc se produit soit avant le rechargement, soit capture quelque chose avant, soit je fais quelque chose de mal. Pour info, j'ai essayé la réponse de Tyler et cela a fonctionné! @FattieLa
dispatch_async(dispatch_get_main_queue())
méthode ci-dessus n'est pas garantie de fonctionner . Je vois un comportement non déterministe avec lui, dans lequel parfois le système a terminé les layoutSubviews et le rendu de la cellule avant le bloc d'achèvement, et parfois après.Voici une solution qui fonctionne à 100% pour moi, sur iOS 10. Elle nécessite la possibilité d'instancier UITableView ou UICollectionView en tant que sous-classe personnalisée. Voici la solution UICollectionView, mais c'est exactement la même chose pour UITableView:
CustomCollectionView.h:
CustomCollectionView.m:
Exemple d'utilisation:
Voir ici pour une version Swift de cette réponse
la source
layoutSubviews
il doit être défini surnil
car les appels ultérieurs àlayoutSubviews
, pas nécessairement dus à l'reloadData
appel, entraîneront l'exécution du bloc car une référence forte est conservée, ce qui n'est pas le comportement souhaité.reloadDataCompletionBlock
un tableau de blocs et à les parcourir lors de l'exécution et à vider le tableau après cela.J'ai eu les mêmes problèmes que Tyler Sheaffer.
J'ai implémenté sa solution dans Swift et cela a résolu mes problèmes.
Swift 3.0:
Swift 2:
Exemple d'utilisation:
la source
if let
en disantreloadDataCompletionBlock?()
qui appellera si ce n'est pas nul 💥self.reloadDataCompletionBlock? { completion() }
aurait dû êtreself.reloadDataCompletionBlock?()
Et une
UICollectionView
version, basée sur la réponse de kolaworld:https://stackoverflow.com/a/43162226/1452758
Besoin de tests. Fonctionne jusqu'à présent sur iOS 9.2, Xcode 9.2 beta 2, avec le défilement d'une collectionView vers un index, en guise de fermeture.
Usage:
la source
Il semble que les gens lisent toujours cette question et les réponses. B / c de cela, je modifie ma réponse pour supprimer le mot synchrone qui est vraiment hors de propos.
When [tableView reloadData]
renvoie, les structures de données internes derrière la tableView ont été mises à jour. Par conséquent, lorsque la méthode est terminée, vous pouvez faire défiler en toute sécurité vers le bas. J'ai vérifié cela dans ma propre application. La réponse largement acceptée de @ rob-mayoff, tout en prêtant à confusion dans la terminologie, reconnaît la même chose dans sa dernière mise à jour.Si votre
tableView
ne défile pas vers le bas, vous pouvez avoir un problème avec un autre code que vous n'avez pas publié. Peut-être que vous modifiez les données une fois le défilement terminé et que vous ne rechargez pas et / ou ne faites pas défiler vers le bas alors?Ajoutez de la journalisation comme suit pour vérifier que les données de la table sont correctes après
reloadData
. J'ai le code suivant dans un exemple d'application et cela fonctionne parfaitement.la source
reloadData
n'est pas synchrone. Il était - voir cette réponse: stackoverflow.com/a/16071589/193896reloadData
retours.reloadData
. Utilisez mon cas de test dansviewWillAppear
accept pour lascrollToRowAtIndexPath:
ligne b / c qui n'a pas de sens si letableView
n'est pas affiché. Vous verrez quereloadData
cela a mis à jour les données mises en cache dans l'tableView
instance et quireloadData
est synchrone. Si vous faites référence à d'autrestableView
méthodes de délégué appelées lors de latableView
mise en page, celles-ci ne seront pas appelées si letableView
n'est pas affiché. Si je ne comprends pas votre scénario, veuillez expliquer.J'utilise cette astuce, je suis sûr que je l'ai déjà postée sur un double de cette question:
la source
En fait, celui-ci a résolu mon problème:
la source
Essayez de cette façon, cela fonctionnera
J'exécuterai quand la table sera complètement chargée
L'autre solution est que vous pouvez sous-classer UITableView
la source
J'ai fini par utiliser une variante de la solution de Shawn:
Créez une classe UITableView personnalisée avec un délégué:
Puis dans mon code, j'utilise
Assurez-vous également de définir votre vue de table sur CustomTableView dans le générateur d'interface:
la source
Dans Swift 3.0 +, nous pouvons créer une extension pour
UITableView
avec unescaped Closure
comme ci-dessous:Et utilisez-le comme ci-dessous où vous voulez:
espérons que cela aidera quelqu'un. à votre santé!
la source
Détails
Solution
Usage
Échantillon complet
Résultats
la source
Juste pour proposer une autre approche, basée sur l'idée que l'achèvement est la cellule «dernière visible» à envoyer
cellForRow
.Un problème possible est le suivant: si
reloadData()
a terminé avant que le nelastIndexPathToDisplay
soit défini, la cellule `` dernière visible '' sera affichée avant d'lastIndexPathToDisplay
être définie et la complétion ne sera pas appelée (et sera en état `` en attente ''):Si nous inversons, nous pourrions finir par déclencher l'achèvement en faisant défiler avant
reloadData()
.la source
Essaye ça:
La couleur de tableView passera du noir au vert uniquement une fois la
reloadData()
fonction terminée.la source
Vous pouvez utiliser la fonction performBatchUpdates de uitableview
Voici comment vous pouvez réaliser
la source
Création d'une extension réutilisable de CATransaction:
Créez maintenant une extension de UITableView qui utiliserait la méthode d'extension de CATransaction:
Usage:
la source
Vous pouvez l'utiliser pour faire quelque chose après le rechargement des données:
la source
Essayez de définir des délais:
la source