web-dev-qa-db-fra.com

Les données ne se chargent pas dans UITableView jusqu'à ce que je défile

J'essaie de charger des données analysées dans des cellules, mais le problème est que cela se produit de manière synchrone et UitableView n'apparaît que lorsque le chargement des données est terminé. J'ai essayé de résoudre le problème en utilisant performSelectorInBackground, mais maintenant les données ne sont pas chargées dans les cellules jusqu'à ce que je commence à faire défiler. Voici mon code:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    [self performSelectorInBackground:@selector(fethchData) withObject:nil];


}


- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
self.listData = nil;
    self.plot=nil;
}


-(void) fethchData

{
    NSError *error = nil;
    NSURL *url=[[NSURL alloc] initWithString:@"http://www.website.com/"];
    NSString *strin=[[NSString alloc] initWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil];

    HTMLParser *parser = [[HTMLParser alloc] initWithString:strin error:&error];

    if (error) {
        NSLog(@"Error: %@", error);
        return;
    }

    listData =[[NSMutableArray alloc] init];
    plot=[[NSMutableArray alloc] init];

    HTMLNode *bodyNode = [parser body];
    NSArray *contentNodes = [bodyNode findChildTags:@"p"];


    for (HTMLNode *inputNode in contentNodes) {
            [plot addObject:[[inputNode allContents] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];    
        }


    NSArray *divNodes = [bodyNode findChildTags:@"h2"];

    for (HTMLNode *inputNode in divNodes) {

            [listData addObject:[[inputNode allContents] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];          

        }
    }


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

    static NSString *CellIdentifier = @"Cell";
    //here you check for PreCreated cell.
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;

    }

    //Fill the cells...  


    cell.textLabel.text = [listData objectAtIndex:indexPath.row];
    cell.textLabel.font = [UIFont boldSystemFontOfSize:14];
    cell.textLabel.numberOfLines=6; 
    cell.textLabel.textColor=[UIColor colorWithHue:0.7 saturation:1 brightness:0.4 alpha:1];


    cell.detailTextLabel.text=[plot objectAtIndex:indexPath.row];
    cell.detailTextLabel.font=[UIFont systemFontOfSize:11];
    cell.detailTextLabel.numberOfLines=6;

    return cell;


}
52
Benjamen

Mettez cela quelque part après que les données ont été chargées avec succès :

dispatch_async(dispatch_get_main_queue(), ^{
    [self.tableView reloadData];
});

Cela corrige le problème de l'appel d'une mise à jour de l'interface graphique lorsque vous n'êtes pas dans le thread principal.

Ce code utilise la technologie GCD de Apple pour forcer la fonction de rechargement des données à s'exécuter sur le thread principal. En savoir plus sur Guide de programmation concurrentielle pour plus de compréhension (c'est un champ assez large) de sorte qu'il est difficile à expliquer dans le commentaire) Quoi qu'il en soit, ce n'est pas très recommandé si vous ne le comprenez pas bien car cela provoque le plantage du programme dans de rares cas.

129
Envil

Pour Swift 3:

DispatchQueue.main.async(execute: { () -> Void in
                self.tableView.reloadData()
            })

Pour Swift 2:

dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.tableView.reloadData()
            })
14
Hamid

Tout ce que vous devez vraiment faire est chaque fois que vous avez une mise à jour de vos données dorsales, appelez

[tableView reloadData];

Comme cela se produit de manière synchrone, vous devriez probablement avoir une fonction comme

-(void) updateTable
{
    [tableView reloadData];
}

et après avoir ajouté les données dans votre appel de téléchargement

[self performSelectorOnMainThread:@selector(updateTable) withObject:nil waitUntilDone:NO];
9
Dan F

U peut utiliser [cell setNeedsDisplay]; par exemple:

dispatch_async(dispatch_get_main_queue(), ^{
            [cell setNeedsDisplay];
            [cell.contentView addSubview:yourView];
        });
2
Devashis Kant

J'ai eu ce problème et je m'en suis occupé toute la journée.

J'utilise des cellules statiques et reloadData provoque un chargement incorrect, il n'affiche que les cellules visibles et supprime les autres. Ce que j'ai remarqué, c'est que lorsque j'ai fait défiler vers le bas (y en valeur négative) les cellules étaient chargées correctement, j'ai donc écrit ce code et cela a fonctionné, même si je n'aime pas le laisser de cette façon.

Tirez si vous trouvez une meilleure solution.

-(void)reloadTableView{
        CGPoint point = self.tableSettings.tableView.contentOffset;
        [self.tableSettings.tableView reloadData];

        [UIView animateWithDuration:.001f animations:^{
            [self.tableSettings.tableView setContentOffset:CGPointMake(point.x, -10)];
        } completion:^(BOOL finished) {
            [UIView animateWithDuration:.001f animations:^{
                [self.tableSettings.tableView setContentOffset:point];
            } completion:^(BOOL finished) {

            }];
        }];
}
1
Arben Pnishi

J'avais exactement le même problème! Je voulais que UITableView soit entièrement rempli avant que le contrôleur de vue n'apparaisse. Le message d'Envil m'a donné les informations dont j'avais besoin, mais ma solution a fini par être différente.

Voici ce que j'ai fait (remodelé pour s'adapter au contexte du demandeur de questions).

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelectorInBackground:@selector(fethchData) withObject:nil];
}

- (void)viewWillAppear {
    [tableView reloadData];
}
1
Jeremy Herrero

Tout d'abord, cela a semi-résolu mon problème connexe. Je souhaite arrondir les coins d'une image dans une cellule de tableau. La distribution asynchrone a résolu le problème pour certaines mais pas toutes les images. Des idées?

Deuxièmement, je pense que vous êtes censé éviter de créer un cycle de référence solide en utilisant une liste de capture de fermeture comme celle-ci:

DispatchQueue.main.async(execute: { [weak weakSelf = self] () -> Void in
            weakSelf!.tableView.reloadData()
        })

Voir: https://developer.Apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//Apple_ref/doc/uid/TP40014097-CH20-ID52

0
user3590685