Data doesn't load in UITableView until I scroll

Objective CUitableviewCocoaSynchronous

Objective C Problem Overview


I am trying to load parsed data in cells, but the problem is that it is happening synchronously and UitableView doesn't show until the data has finished loading. I tried to solve the problem by using performSelectorInBackground, but now data isn't loaded in the cells until I start scrolling. Here is my 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;
    

}

Objective C Solutions


Solution 1 - Objective C

Put this somewhere after the data is loaded successfully:

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

This fix the problem of calling a GUI update while you're not in the main thread.

This code uses the GCD Technology from Apple to force the reload data function to run on main thread. Read more about Concurrency Programming Guide for more understanding (it's quite large field so that it's hard to explain in the comment) Anyway, it's not very recommended if you don't understand it well because it causes the program to crash some rare cases.

Solution 2 - Objective C

For swift 3:

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

For swift 2:

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

Solution 3 - Objective C

All you really need to do is any time you have an update to your back-end data, call

[tableView reloadData];

Since this is happening synchronously, you should probably have a function like

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

and after adding the data in your download call

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

Solution 4 - Objective C

U can use [cell setNeedsDisplay]; for example:

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

Solution 5 - Objective C

I had this problem and I was dealing with it all the day.

I am using static cells and reloadData is causing the wrong loading, it displays only the visible cells and remove the others. What I noticed is that when I scrolled down (y in negative value) the cells where loaded correctly, so I wrote this code and it worked, even though I don't like to let it in this way.

Shoot if you find any better 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) {
                
            }];
        }];
}

Solution 6 - Objective C

I was having the exact same problem! I wanted the UITableView to be fully populated before the view controller appeared. Envil's post gave me the information I needed, but my solution ended up being different.

Here's what I did (remodeled to fit the context of the question asker).

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

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

Solution 7 - Objective C

First, this semi-solved my related problem. I want to round corners of an image in a table cell. Dispatching asynchronously fixed the problem for some but not all of the images. Any ideas?

Second, I think you are supposed to avoid creating a strong reference cycle by using a closure capture list like this:

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

See: https://developer.apple.com/library/prerelease/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID52

Solution 8 - Objective C

I experienced the same issue when using self-sizing cells, and I found that setting the estimatedHeight to 50 would fix the problem.

Set estimatedHeight on the tableView itself or return an estimate from estimatedHeightForRowAt in your UITableViewDelegate.

It seems to work as long as the estimate is more than 0.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBenjamenView Question on Stackoverflow
Solution 1 - Objective CEnvilView Answer on Stackoverflow
Solution 2 - Objective CHamidView Answer on Stackoverflow
Solution 3 - Objective CDan FView Answer on Stackoverflow
Solution 4 - Objective CDevashis KantView Answer on Stackoverflow
Solution 5 - Objective CArben PnishiView Answer on Stackoverflow
Solution 6 - Objective CJeremy HView Answer on Stackoverflow
Solution 7 - Objective Cuser3590685View Answer on Stackoverflow
Solution 8 - Objective CLaffenView Answer on Stackoverflow