Recevoir une notification lorsque UITableView a fini de demander des données?


108

Existe-t-il un moyen de savoir quand a UITableViewa fini de demander des données à sa source de données?

Aucune des méthodes viewDidLoad/ viewWillAppear/ viewDidAppeardu contrôleur de vue associé ( UITableViewController) n'est utile ici, car elles se déclenchent toutes trop tôt. Aucun d'entre eux ne garantit (tout à fait compréhensible) que les requêtes adressées à la source de données sont terminées pour le moment (par exemple, jusqu'à ce que la vue défile).

Une solution que j'ai trouvé est d'appeler reloadDataà viewDidAppear, puisque, lorsque les reloadDatarendements, la vue de la table est assuré d'avoir fini d' interroger la source de données autant qu'il doit pour l'instant.

Cependant, cela semble plutôt désagréable, car je suppose que cela oblige la source de données à se voir demander les mêmes informations deux fois (une fois automatiquement et une fois à cause de l' reloadDataappel) lors de son premier chargement.

La raison pour laquelle je veux faire cela est que je veux conserver la position de défilement du UITableView- mais jusqu'au niveau du pixel, pas seulement jusqu'à la ligne la plus proche.

Lors de la restauration de la position de défilement (en utilisant scrollRectToVisible:animated:), j'ai besoin que la vue de la table contienne déjà suffisamment de données, sinon l' scrollRectToVisible:animated:appel de méthode ne fait rien (ce qui se passe si vous placez l'appel seul dans l'un des viewDidLoad, viewWillAppearou viewDidAppear).


Il semble que vous cherchez quelque chose de similaire à ce stackoverflow.com/a/11672379/2082172 .
Timur Kuchkarov

D'après mon expérience, UITableView peut mettre en cache les appels à reloadData, tout comme il met en cache les appels pour insérer / supprimer des lignes et d'autres choses. UITableView appellera le délégué de source de données lorsqu'il sera prêt, en fonction de l'utilisation du processeur et d'autres choses.
Walt Sellers

Réponses:


61

Cette réponse ne semble plus fonctionner, en raison de certaines modifications apportées à l'implémentation de UITableView depuis que la réponse a été écrite. Voir ce commentaire: Être averti lorsque UITableView a fini de demander des données?

Je joue avec ce problème depuis quelques jours et je pense que le sous UITableView-classement reloadDataest la meilleure approche:

- (void)reloadData {

    NSLog(@"BEGIN reloadData");

    [super reloadData];

    NSLog(@"END reloadData");

}

reloadDatane se termine pas avant que la table n'ait fini de recharger ses données. Ainsi, lorsque le second NSLogest déclenché, la vue de table a en fait fini de demander des données.

J'ai sous-classé UITableViewpour envoyer des méthodes au délégué avant et après reloadData. Il fonctionne comme un charme.


2
Je dois dire que cela semble être la meilleure solution. Je viens de l'implémenter et, comme vous le dites, cela fonctionne comme un charme. Merci!
théorie

2
@EricMORAND Vous dites que "reloadData ne se termine pas avant que la table n'ait fini de recharger ses données." Pouvez-vous clarifier ce que vous entendez par là? Je trouve que cela reloadDatarevient immédiatement et je vois "END reloadData" avant que les cellules ne soient réellement rechargées (c'est-à-dire avant que les UITableViewDataSourceméthodes ne soient appelées). Mon expérimentation démontre exactement le contraire de ce que vous dites. Je dois mal comprendre ce que vous essayez de dire.
Rob

3
La réponse d'Eric n'est-elle pas la même chose que de ne pas implémenter (lire et non de remplacer) reloaddata, d'appeler reloaddata (qui sera essentiellement [super reloaddata], puis après l'avoir appelé, faites ce que vous voulez à la fin?)
Nirav Bhatt

5
Il était une fois, reloadData a terminé le processus de chargement avant la fin. Mais Apple l'a changé à un moment donné. Désormais, la classe UITableView peut mettre en cache l'appel reloadData avec tous les appels d'insertion et de suppression de lignes. Si vous regardez la déclaration @interface pour UITableView, vous trouverez le membre NSMutableArray _reloadItems juste sous _insertItems et _deleteItems. (J'ai dû retravailler le code dont j'ai hérité à cause de ce changement.)
Walt Sellers

3
Affichage du code d'achèvement dans un bloc sur la file d' attente principale après l' appel [super reloadData]fonctionne pour moi: dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});. Cela saute essentiellement aux blocs qui sont affichés par la vue de la table reloadData.
Timothy Moose

53

J'ai eu le même scénario dans mon application et j'ai pensé publier ma réponse pour vous les gars, car les autres réponses mentionnées ici ne fonctionnent pas pour moi pour iOS7 et versions ultérieures

Enfin, c'est la seule chose qui a fonctionné pour moi.

[yourTableview reloadData];

dispatch_async(dispatch_get_main_queue(),^{
        NSIndexPath *path = [NSIndexPath indexPathForRow:yourRow inSection:yourSection];
        //Basically maintain your logic to get the indexpath
        [yourTableview scrollToRowAtIndexPath:path atScrollPosition:UITableViewScrollPositionTop animated:YES];
 });

Mise à jour rapide:

yourTableview.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
    let path : NSIndexPath = NSIndexPath(forRow: myRowValue, inSection: mySectionValue)
    //Basically maintain your logic to get the indexpath
    yourTableview.scrollToRowAtIndexPath(path, atScrollPosition: UITableViewScrollPosition.Top, animated: true)

})

Alors, comment cela fonctionne.

Fondamentalement, lorsque vous rechargez, le thread principal devient occupé, donc à ce moment-là, lorsque nous effectuons un thread async de répartition, le bloc attendra que le thread principal soit terminé. Donc, une fois que la tableview a été complètement chargée, le thread principal se terminera et il enverra donc notre bloc de méthode

Testé sous iOS7 et iOS8 et cela fonctionne à merveille ;)

Mise à jour pour iOS9: Cela fonctionne très bien avec iOS9 également. J'ai créé un exemple de projet dans github en tant que POC. https://github.com/ipraba/TableReloadingNotifier

Je joins ici la capture d'écran de mon test.

Environnement testé: simulateur iOS9 iPhone6 ​​de Xcode7

entrez la description de l'image ici


8
meilleur answear que j'ai trouvé!
Nikolay Shubenkov

@Gon j'ai fait un test dans iOS9 et cela fonctionne très bien. Pouvez-vous s'il vous plaît se référer à github.com/ipraba/TableReloadingNotifier
ipraba

Cela fonctionne bien sur mon émulateur mais ne semble pas fonctionner sur l'appareil réel. Quelqu'un d'autre a-t-il ce problème?
sosale151

1
Cette solution fonctionne enfin pour moi! il fonctionne bien sur iOS 9
Forte

1
J'ai testé sur Real Device, un iPhone 6s. Cela fonctionne bien aussi.
Fort

25

EDIT: Cette réponse n'est en fait pas une solution. Cela semble probablement fonctionner au début car le rechargement peut se produire assez rapidement, mais en fait, le bloc d'achèvement n'est pas nécessairement appelé une fois que le rechargement des données est complètement terminé - car reloadData ne se bloque pas. Vous devriez probablement chercher une meilleure solution.

Pour développer la réponse de @Eric MORAND, mettons un bloc de complétion. Qui n'aime pas un bloc?

@interface DUTableView : UITableView

   - (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;

@end

et...

#import "DUTableView.h"

@implementation DUTableView

- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [super reloadData];
    if(completionBlock) {
        completionBlock();
    }
}

@end

Usage:

[self.tableView reloadDataWithCompletion:^{
                                            //do your stuff here
                                        }];

2
Je ne peux pas avoir assez de blocs. Qui a besoin d'un délégué quand vous avez un joli bloc!
bandejapaisa

J'aime ça, mais qu'en est-il des moments où le système appelle reloadData (), par exemple lorsque la table est affichée pour la première fois?
Symétrique

12
Ce n'est pas la solution Le bloc Completition commence l'exécution avant cellForRowAtIndexPath
zvjerka24

1
Cela ne fonctionnera pas; reloadData n'est pas une méthode de blocage. Ce code est appelé immédiatement après l'appel de reloadData, même si les cellules ne sont pas encore rechargées. En outre, regardez ce code, et vous verrez que vous pourriez aussi bien mettre votre code après reloadData .
colinta

1
Cela fonctionne pour moi avec un changement mineur. Au lieu d'appeler le bloc d'achèvement directement, appeler dans un bloc affiché sur la file d' attente principale: dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});. Ce saut saute essentiellement dans les blocs affichés par la vue de la table reloadData.
Timothy Moose

11

reloadData demande simplement des données pour les cellules visibles. Dit, pour être averti lorsque la partie spécifiée de votre table est chargée, veuillez accrocher la tableView: willDisplayCell:méthode.

- (void) reloadDisplayData
{
    isLoading =  YES;
    NSLog(@"Reload display with last index %d", lastIndex);
    [_tableView reloadData];
    if(lastIndex <= 0){
    isLoading = YES;
    //Notify completed
}

- (void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(indexPath.row >= lastIndex){
    isLoading = NO;
    //Notify completed
}

1
Je ne sais pas si cela fonctionne. Le dernier index est la fin des données de la table (par exemple 100 enregistrements) mais la table n'affichera que ce qui est visible à l'écran (par exemple 8 enregistrements).
Travis M.

10

Telle est ma solution. 100% fonctionne et utilisé dans de nombreux projets. C'est une simple sous-classe UITableView.

@protocol MyTableViewDelegate<NSObject, UITableViewDelegate>
@optional
- (void)tableViewWillReloadData:(UITableView *)tableView;
- (void)tableViewDidReloadData:(UITableView *)tableView;
@end

@interface MyTableView : UITableView {
    struct {
        unsigned int delegateWillReloadData:1;
        unsigned int delegateDidReloadData:1;
        unsigned int reloading:1;
    } _flags;
}
@end

@implementation MyTableView
- (id<MyTableViewDelegate>)delegate {
    return (id<MyTableViewDelegate>)[super delegate];
}

- (void)setDelegate:(id<MyTableViewDelegate>)delegate {
    [super setDelegate:delegate];
    _flags.delegateWillReloadData = [delegate respondsToSelector:@selector(tableViewWillReloadData:)];
    _flags.delegateDidReloadData = [delegate    respondsToSelector:@selector(tableViewDidReloadData:)];
}

- (void)reloadData {
    [super reloadData];
    if (_flags.reloading == NO) {
        _flags.reloading = YES;
        if (_flags.delegateWillReloadData) {
            [(id<MyTableViewDelegate>)self.delegate tableViewWillReloadData:self];
        }
        [self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];
    }
}

- (void)finishReload {
    _flags.reloading = NO;
    if (_flags.delegateDidReloadData) {
        [(id<MyTableViewDelegate>)self.delegate tableViewDidReloadData:self];
    }
}

@end

C'est similaire à la solution de Josh Brown à une exception près. Aucun délai n'est nécessaire dans la méthode performSelector. Peu importe le temps qu'il reloadDatafaut. tableViewDidLoadData:se déclenche toujours lorsque vous tableViewavez fini de demander dataSource cellForRowAtIndexPath.

Même si vous ne voulez pas de sous-classe, UITableViewvous pouvez simplement appeler [performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]et votre sélecteur sera appelé juste après le rechargement de la table. Mais vous devez vous assurer que le sélecteur n'est appelé qu'une seule fois par appel à reloadData:

[self.tableView reloadData];
[self performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f];

Prendre plaisir. :)


1
L'utilisation de l'appel performSelector est géniale. Simple et fonctionnel, merci
Julia

C'est absolument génial. Je me dispute avec ça depuis des jours. Je vous remercie!
David Carrico

@MarkKryzhanouski, avez-vous testé sur iOS 9?
Victor

@MarkKryzhanouski, merci pour les commentaires rapides! Fonctionne très bien sur iOS 7 et 8.
Victor

Les solutions performSelectorou s'exécutant sur le thread principal avec dispatch_asynchne fonctionnent pas sous iOS 9 .
Manuel

8

C'est une réponse à une question légèrement différente: j'avais besoin de savoir quand UITableViewj'avais également fini d'appeler cellForRowAtIndexPath(). J'ai sous layoutSubviews()-classé (merci @Eric MORAND) et ajouté un rappel de délégué:

SDTableView.h:

@protocol SDTableViewDelegate <NSObject, UITableViewDelegate>
@required
- (void)willReloadData;
- (void)didReloadData;
- (void)willLayoutSubviews;
- (void)didLayoutSubviews;
@end

@interface SDTableView : UITableView

@property(nonatomic,assign) id <SDTableViewDelegate> delegate;

@end;

SDTableView.m:

#import "SDTableView.h"

@implementation SDTableView

@dynamic delegate;

- (void) reloadData {
    [self.delegate willReloadData];

    [super reloadData];

    [self.delegate didReloadData];
}

- (void) layoutSubviews {
    [self.delegate willLayoutSubviews];

    [super layoutSubviews];

    [self.delegate didLayoutSubviews];
}

@end

Usage:

MyTableViewController.h:

#import "SDTableView.h"
@interface MyTableViewController : UITableViewController <SDTableViewDelegate>
@property (nonatomic) BOOL reloadInProgress;

MyTableViewController.m:

#import "MyTableViewController.h"
@implementation MyTableViewController
@synthesize reloadInProgress;

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

    if ( ! reloadInProgress) {
        NSLog(@"---- numberOfSectionsInTableView(): reloadInProgress=TRUE");
        reloadInProgress = TRUE;
    }

    return 1;
}

- (void)willReloadData {}
- (void)didReloadData {}
- (void)willLayoutSubviews {}

- (void)didLayoutSubviews {
    if (reloadInProgress) {
        NSLog(@"---- layoutSubviewsEnd(): reloadInProgress=FALSE");
        reloadInProgress = FALSE;
    }
}

REMARQUES: Comme il s'agit d'une sous-classe UITableViewdont une propriété de délégué pointe déjà, MyTableViewControlleril n'est pas nécessaire d'en ajouter une autre. Le "délégué @dynamic" indique au compilateur d'utiliser cette propriété. (Voici un lien décrivant ceci: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )

La UITableViewpropriété dans MyTableViewControllerdoit être modifiée pour utiliser la nouvelle SDTableViewclasse. Cela se fait dans l'inspecteur d'identité d'Interface Builder. Sélectionnez l' UITableViewintérieur du UITableViewControlleret réglez sa "Classe personnalisée" sur SDTableView.


Où définissez-vous votre MyTableViewController pour être le délégué pour le SDTableView? Comment se fait-il qu'il soit possible de définir une propriété de nom "délégué" sur SDTableView, alors que sa superclasse a déjà une propriété de ce nom (UITableView.delegate)? Comment attacher votre SDTableView personnalisé à la propriété MyTableViewController.tableView lorsque la propriété est de type "UITablewView" et que l'objet (une instance de SDTableView) est de type "SDTableView"? Je lutte contre le même problème, donc j'espère qu'il y aura une solution à ces problèmes :)
Earl Grey

Dans le code de Symmetric (SDTableView), reloadInProgress ne doit-il pas être défini sur FALSE dans didReloadData au lieu de didLayoutSubviews? Parce que reloadData est appelé après layoutSubviews et que le chargement ne doit pas être considéré comme terminé avant la fin de reloadData.
tzuchien.chiu

Cela ne fonctionne pas pour iOS 8, a dû intégrer la réponse d'iPrabu .
Stunner

6

J'avais trouvé quelque chose de similaire pour recevoir une notification de changement contentSizede TableView. Je pense que cela devrait également fonctionner ici, car contentSize change également avec le chargement des données.

Essaye ça:

En viewDidLoadécriture,

[self.tableView addObserver:self forKeyPath:@"contentSize" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld | NSKeyValueObservingOptionPrior context:NULL];

et ajoutez cette méthode à votre viewController:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"contentSize"]) {
        DLog(@"change = %@", change.description)

        NSValue *new = [change valueForKey:@"new"];
        NSValue *old = [change valueForKey:@"old"];

        if (new && old) {
            if (![old isEqualToValue:new]) {
                // do your stuff
            }
        }


    }
}

Vous pourriez avoir besoin de légères modifications lors de la vérification des modifications. Cela avait fonctionné pour moi cependant.

À votre santé! :)


1
Très intelligent. Fonctionne parfaitement pour moi. Merci d'avoir partagé!
DZenBot

2
Élégant! La seule chose que j'ajouterais est que vous devez vous supprimer vous-même et un observateur (peut-être dans la méthode -dealloc). Ou ajoutez-vous en tant qu'observateur dans le -viewWillAppearet supprimez-vous dans la -viewWillDisapearméthode.
Loozie

2

Voici une solution possible, bien que ce soit un hack:

[self.tableView reloadData];
[self performSelector:@selector(scrollTableView) withObject:nil afterDelay:0.3];

Où votre -scrollTableViewméthode fait défiler la vue du tableau avec -scrollRectToVisible:animated:. Et, bien sûr, vous pouvez configurer le délai dans le code ci-dessus de 0,3 à tout ce qui semble fonctionner pour vous. Ouais, c'est ridiculement piraté, mais ça marche pour moi sur mon iPhone 5 et 4S ...


2
Cela peut sembler "hacky", mais c'est vraiment une approche standard pour le défilement: le reporter au prochain cycle d'exécution. Je changerais le délai à 0, car cela fonctionne aussi bien et a moins de latence.
phatmann

Cela n'a pas fonctionné pour moi avec le délai réglé sur 0; 0,3 était le plus bas que je pouvais aller et toujours obtenir les données.
Josh Brown

@phatmann Cela a fonctionné pour moi. J'ai également pu utiliser 0 pour mon retard. Merci à vous deux.
mcphersonjr

1

J'avais quelque chose de similaire, je crois. J'ai ajouté un BOOL comme variable d'instance qui me dit si l'offset a été restauré et l'enregistre -viewWillAppear:. Lorsqu'il n'a pas été restauré, je le restaure dans cette méthode et définissez le BOOL pour indiquer que j'ai récupéré le décalage.

C'est une sorte de hack et cela peut probablement être mieux fait, mais cela fonctionne pour moi pour le moment.


Ouais, le problème est que viewWillAppear semble être trop tôt (du moins dans mon scénario). Essayer de restaurer le décalage dans viewWillAppear ne fait rien - à moins que j'ajoute le hack d'appeler reloadData en premier. Si je comprends bien, viewWillAppear / viewDidAppear se réfère uniquement à la vue de tableau elle-même apparaissant réellement - ils ne font aucune déclaration quant à savoir si les cellules du tableau dans la vue ont été énumérées ou remplies ... et cela doit se produire avant que vous puissiez récupérer un décalage, sinon vous récupéreriez un décalage sur une vue vide (et je comprends pourquoi cela ne fonctionnera pas!).
kennethmac2000 le

Mais peut-être voulez-vous dire que vous forcez également le chargement des cellules du tableau en appelant reloadData dans viewWillAppear?
kennethmac2000 le

Non, je ne force pas une recharge. Quand j'ai eu le problème, j'ai essayé de restaurer le décalage dans -viewDidLoad(où cela devrait se produire bien sûr) mais cela ne fonctionnait que lorsque je définissais le décalage animé. En déplaçant le réglage du décalage, -viewWillAppear:cela fonctionnait, mais je devais maintenir un drapeau pour ne le définir qu'une seule fois. Je suppose que la vue de table recharge ses données une fois ajoutées à une vue, donc c'est déjà dedans -loadView. Êtes-vous sûr que vos données sont disponibles lors du chargement de la vue? Ou est-il chargé dans un thread séparé ou quelque chose?
Joost

OK, peut-être pouvez-vous aider ma compréhension ici. C'est comme ça que je comprends la séquence d'appels quand un UITableView est construit. 1) viewDidLoad est déclenché en premier. Cela indique que l'UITableView est chargé en mémoire. 2) viewWillAppear est à côté du feu. Cela indique que UITableView sera affiché, mais pas nécessairement que tous les objets UITableViewCell visibles seront entièrement instanciés / rendu terminé.
kennethmac2000 le

2
Et tout ce qui précède est vrai, la question est alors: quelles options non-hacky avons-nous pour savoir quand l'UITableView a obtenu suffisamment d'informations de sa source de données pour garantir qu'une requête de défilement (c'est-à-dire un scrollRectToVisible: animated: call ) fonctionnera réellement (et ne fera simplement rien)?
kennethmac2000

1

Il semble que vous souhaitiez mettre à jour le contenu des cellules, mais sans les sauts soudains qui peuvent accompagner les insertions et les suppressions de cellules.

Il existe plusieurs articles sur ce sujet. C'est un.

Je suggère d'utiliser setContentOffset: animated: au lieu de scrollRectToVisible: animated: pour les paramètres au pixel près d'une vue de défilement.


1

Vous pouvez essayer la logique suivante:

-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyIdentifier"];

    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"MyIdentifier"];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    if ( [self chkIfLastCellIndexToCreate:tableView :indexPath]){
        NSLog(@"Created Last Cell. IndexPath = %@", indexPath);
        //[self.activityIndicator hide];
        //Do the task for TableView Loading Finished
    }
    prevIndexPath = indexPath;

    return cell;
}



-(BOOL) chkIfLastCellIndexToCreate:(UITableView*)tableView : (NSIndexPath *)indexPath{

    BOOL bRetVal = NO;
    NSArray *visibleIndices = [tableView indexPathsForVisibleRows];

    if (!visibleIndices || ![visibleIndices count])
        bRetVal = YES;

    NSIndexPath *firstVisibleIP = [visibleIndices objectAtIndex:0];
    NSIndexPath *lastVisibleIP = [visibleIndices objectAtIndex:[visibleIndices count]-1];

    if ((indexPath.row > prevIndexPath.row) && (indexPath.section >= prevIndexPath.section)){
        //Ascending - scrolling up
        if ([indexPath isEqual:lastVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    } else if ((indexPath.row < prevIndexPath.row) && (indexPath.section <= prevIndexPath.section)) {
        //Descending - scrolling down
        if ([indexPath isEqual:firstVisibleIP]) {
            bRetVal = YES;
            //NSLog(@"Last Loading Cell :: %@", indexPath);
        }
    }
    return bRetVal;
}

Et avant d'appeler reloadData, définissez prevIndexPath sur nil. Comme:

prevIndexPath = nil;
[mainTableView reloadData];

J'ai testé avec NSLogs, et cette logique semble correcte. Vous pouvez personnaliser / améliorer au besoin.


0

enfin j'ai fait fonctionner mon code avec ceci -

[tableView scrollToRowAtIndexPath:scrollToIndex atScrollPosition:UITableViewScrollPositionTop animated:YES];

il y avait peu de choses dont il fallait s'occuper -

  1. appelez-le dans " - (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath"
  2. assurez-vous simplement que le message "scrollToRowAtIndexPath" est envoyé à l'instance appropriée de UITableView, qui est définitivement MyTableview dans ce cas.
  3. Dans mon cas, UIView est la vue qui contient une instance de UITableView
  4. En outre, cela sera appelé pour chaque charge de cellule. Par conséquent, placez une logique à l'intérieur de «cellForRowAtIndexPath» pour éviter d'appeler «scrollToRowAtIndexPath» plusieurs fois.

et assurez-vous simplement que cette pièce n'est pas appelée plus d'une fois. sinon il verrouille le défilement.
smile.al.d.way

Cela peut fonctionner, mais ce n'est certainement pas élégant et ce n'est pas la solution que je recherche. Malheureusement, il ne semble pas y avoir de meilleur moyen de déterminer si -reloadData a réellement fini d'obtenir les données ...
Josh Brown

0

Vous pouvez redimensionner votre tableview ou définir sa taille de contenu dans cette méthode lorsque toutes les données sont chargées:

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    tableView.frame =CGRectMake(tableView.frame.origin.x, tableView.frame.origin.y, tableView.frame.size.width, tableView.contentSize.height);
}

0

Je viens d'exécuter une minuterie planifiée répétée et de l'invalider uniquement lorsque la taille de contenu de la table est plus grande lorsque la hauteur de tableHeaderView (signifie qu'il y a du contenu de lignes dans la table). Le code en C # (monotouch), mais j'espère que l'idée est claire:

    public override void ReloadTableData()
    {
        base.ReloadTableData();

        // don't do anything if there is no data
        if (ItemsSource != null && ItemsSource.Length > 0)
        {
            _timer = NSTimer.CreateRepeatingScheduledTimer(TimeSpan.MinValue, 
                new NSAction(() => 
                {
                    // make sure that table has header view and content size is big enought
                    if (TableView.TableHeaderView != null &&
                        TableView.ContentSize.Height > 
                            TableView.TableHeaderView.Frame.Height)
                    {
                        TableView.SetContentOffset(
                            new PointF(0, TableView.TableHeaderView.Frame.Height), false);
                        _timer.Invalidate();
                        _timer = null;
                    }
                }));
        }
    }

0

N'est-il pas UITableView layoutSubviewsappelé juste avant que la vue de tableau affiche son contenu? J'ai remarqué qu'il est appelé une fois que la vue table a fini de charger ses données, vous devriez peut-être enquêter dans cette direction.


0

Depuis iOS 6, la UITableviewméthode déléguée a appelé:

-(void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section

s'exécutera une fois que votre table sera rechargée avec succès. Vous pouvez effectuer la personnalisation selon les besoins dans cette méthode.


0

La meilleure solution que j'ai trouvée dans Swift

extension UITableView {
    func reloadData(completion: ()->()) {
        self.reloadData()
        dispatch_async(dispatch_get_main_queue()) {
            completion()
        }
    }
}

-1

Pourquoi ne pas simplement prolonger?

@interface UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock;
@end

@implementation UITableView(reloadComplete)
- (void) reloadDataWithCompletion:( void (^) (void) )completionBlock {
    [self reloadData];
    if(completionBlock) {
        completionBlock();
    }
}
@end

faites défiler jusqu'à la fin:

[self.table reloadDataWithCompletion:^{
    NSInteger numberOfRows = [self.table numberOfRowsInSection:0];
    if (numberOfRows > 0)
    {
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:numberOfRows-1 inSection:0];
        [self.table scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:NO];
    }
}];

Non testé avec beaucoup de données

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.