Question

I have a UITableView that loads its array items fine on the first call. However when i refresh the tableview it crashes because the delegate method cellForRowAtIndexPath gets called to early. I have an integer which represents the index of my main data array and this gets reset to 0 in my refresh button. However its crashing because its trying to reload the data before its been reset. I would normally use indexPath.row as my index however the array is complicated and the indexPath.row will not match up to what i want to show for each cell. Heres some code, any help is appreciated.

This gets called when i pull down to refresh AND in viewDidLoad to prepare the data

- (IBAction)refresh:(id)sender {
itemIndexer = 0;
[sender beginRefreshing];
[self loadData];
}

Part of my loadData method

-(void) loadData {
dispatch_queue_t backgroundQueue = dispatch_queue_create("com.Foo.myqueue", 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();

dispatch_async(backgroundQueue, ^{

   [self downloadData];

    dispatch_async(mainQueue, ^{
            [self.tableView reloadData];
            [_rcRefresh endRefreshing];
    });
});

In viewDidLoad i call to initially load the tableview:

[self refresh:_rcRefresh];

I am getting a index outside bounds of array error. Which i have used breakpoints to determine why, the reason is simply because the refresh isn't getting called at all otherwise itemIndexer would be set to 0. Instead its a number 1 greater than the array size. If its really necessary i can post cellForRowAtIndexPath however I'm unsure if you need it if it works on the first call. So to summarise the first call to the tableview works and the data is loaded fine however the refresh causes the index to be outside the bounds of the array as if refresh: is never called.

Was it helpful?

Solution

For what you say, I can only try guessing:

  • Your numberOfSectionsInTableView/numberOfRowsInSection is returning a wrong number.

  • If [self downloadData] is asynchronous (you are making a server request and not waiting for the response), you should reloadData once you have the data.

  • The data you download is not merged properly with the data you already have.

Some more code (numberOfSectionsInTableView, numberOfRowsInSection, cellForRowAtIndexPath, downloadData) would definitely help.

OTHER TIPS

Couple of points for clarity...

  • As indicated by @k20, it is not that tableView:cellForRowAtIndexPath: is called too early, but that you need to better manage your download data once your asynchronous process / method has completed.
  • tableView:cellForRowAtIndexPath: is a UITableView data source method, not a delegate method. It is worth mentioning this pedantic detail because it may help you or others better understand the code you are writing. ;)

The table view method calls are what they are - as I understand it, all table view methods are called in order, each time a UITableViewController is init or awakeFromNib. Those that you must override (tableView:numberOfRowsInSection: & tableView:cellForRowAtIndexPath:), and those that you choose to override will still execute in that same order.

Therefore a more appropriate title for your question might be... "How to update a UITableView with data from a download on an asynchronous thread."

Again @k20 is pointing you to the correct solution. Have you attempting placing these two lines of code...

        [self.tableView reloadData];
        [_rcRefresh endRefreshing];

within your async call, instead of back in the main queue?

It may be that your code as written is executing like this...

  1. Prepare local variables for dispatch_q_t;
  2. Commence download process;
  3. reload data for table view;
  4. end the refresh [_rcRefresh endRefreshing];
  5. depending on time it takes, then finish download process;

Where you obviously would like to execute like this...

  1. Prepare local variables for dispatch_q_t;
  2. Commence download process;
  3. depending on time it takes, finish download process;
  4. reload data for table view;
  5. end the refresh [_rcRefresh endRefreshing];

Try my suggestion and let me know how you go. Hope that helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top