25

I have a UITableview that I load with data async so the tableview might appear without data.
I have tired the ReloadData method but the tableview remains empty until I scroll the tableview, suddenly the data appears.
The same thing happens when I load a tableview as a detailedview and switching between items, the previoud items data appears first and as soon as I scroll in the table view it shows the correct data.
My guess is that the ReloadData method works just fine, but I need to redraw the tableview somehow, any suggestions on how to solve this?

/Jimmy

sergtk
  • 10,714
  • 15
  • 75
  • 130
Jimmy Engtröm
  • 1,998
  • 4
  • 23
  • 34

6 Answers6

56

You said you're populating content asynchronously but did you invoke the reloadData in the context of the main thread ? (and not via the thread that populates the content)

Objective-C

[yourUITableView performSelectorOnMainThread:@selector(reloadData)
                                  withObject:nil 
                               waitUntilDone:NO];

Swift

dispatch_async(dispatch_get_main_queue(), { self.tableView.reloadData() })

Monotouch

InvokeOnMainThread(() => this.TableView.ReloadData());
Gil Sand
  • 5,802
  • 5
  • 36
  • 78
yonel
  • 7,855
  • 2
  • 44
  • 51
  • 7
    Thanks that solved it =) the solution for monotouch is: InvokeOnMainThread(() => this.TableView.ReloadData()); – Jimmy Engtröm Feb 24 '10 at 11:37
  • @yonel I'm having the same issue as JimmyEngtröm and I was really hoping what you said would do it, but alas I still have this issue. I can find no way of refreshing the UITableView after I update the underlying data arrays it is using. – Mason G. Zhwiti Oct 11 '11 at 07:00
  • @MasonG.Zhwiti: set a breakpoint in your numberOfRowsInSections method in your table datasource, and check if it goes in it after the reloadData. Then check the number of rows you return here, if it's not 0, then it will call the cellForRowAtIndex. – yonel Oct 11 '11 at 07:14
  • In my opinion this is a workaround, when the real problem is that you probably shouldn't be making any UI updates whilst still on the background thread. I added another answer that explains more below. – Kyle Clegg Mar 05 '14 at 23:47
  • 1
    To do this in swift, use `dispatch_async(dispatch_get_main_queue(), { self.tableView.reloadData() })` – SimplGy May 24 '15 at 18:39
4

Yonels answer is perfect when your view is currently visible to the user (e.g: User presses a reload button which populates your UITableView.)

However, if your data is loaded asynchronously and your UITableView is not visible during the update (e.g: You add Data to your UITableView in another View and the UITableView is displayed later by userinput), simply override the UITableViewController's viewWillAppear method.

- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.tableView reloadData];
}

The positive effect is that your UITableView only reloads it's data once when the user actually want's to see it, not when new items are added.

Josh Earl
  • 18,151
  • 15
  • 62
  • 91
Jay
  • 2,519
  • 5
  • 25
  • 42
2

I had the similar issue today(Storyboard in iOS 7.0). I was using table View inside a UIViewController.When the view was loaded everything was working fine; all the delegates were getting called.However ; When the underlying dataSource(in my case,Array) was modified; the visible table rows were not getting updated.They were updated; only when I was scrolling the table view.

Tried everything; calling reloadData on Main thread;calling reload in ViewWillAppear; nothing worked.

The issue that I found ; was that I had not made the connection in storyboard; for the table view with the reference Outlet.The dataSource and delegate were set though. I did not think that it could be the issue; as everything was working fine at the first go.

Hope it helps someone.I had some terrible time to find this out.

RUppal
  • 71
  • 4
2

I guess it wasn't reloaded.

When you scroll the cells to out of screen, then…

tableView: cellForRowAtIndexPath:

…will be called. So it will be reloaded.

I guess UITableView variable is not validated.

If you use UITableView as a main view, you can try this.

[self.view reloadData];

or

[self.tableView reloadData];

Alex Zavatone
  • 4,106
  • 36
  • 54
Joey
  • 2,912
  • 2
  • 27
  • 32
2

Swift 4:

DispatchQueue.main.async { self.tableView.reloadData() }
Nate Uni
  • 933
  • 1
  • 12
  • 26
0

After somewhat naively copying in yonel's solution and calling it good I realized that calling performSelectorOnMainThread:withObject:waitUntilDone: fixed the symptom, but not the problem. The bigger problem is that you are making UI updates while still in the context of the asynchronous or background thread.

This is what my code looked like:

dispatch_queue_t queue = dispatch_queue_create("com.kyleclegg.myqueue", NULL);
dispatch_async(queue, ^{

  // Make API call
  // Retrieve data, parse JSON, update local properties
  // Make a call to reload table data

});

When it should look like this:

dispatch_queue_t queue = dispatch_queue_create("com.kyleclegg.myqueue", NULL);
dispatch_async(queue, ^{

  // Make API call
  // Retrieve data, parse JSON, update local properties

  dispatch_async(dispatch_get_main_queue(), ^{
    // Now make the call to reload data and make any other UI updates
    [self.tableView reloadData]
  });

});

If the only thing you need to do is call [self.tableView reloadData] it's probably fine to use performSelectorOnMainThread:withObject:waitUntilDone: since it accomplishes the same goal, but you should also recognize what's happening in the big picture. Also if you are doing more UI work than just reloading the table then all of that code should go on the main queue as well.

Reference: A concise example of using GCD and managing the background vs. main thread.

Kyle Clegg
  • 38,547
  • 26
  • 130
  • 141