So here is my problem. In my app home page I have a simple UITableView that can hold maximum 3 sections, 2 sections with a maximum of 3 items each and the third section can hold a maximum of 6 items.
Each section has a header associated with.
The actual content comes from web, I make a request, the server sends data, I parse the response with NSXMLParser, create the arrays to hold data for each section (3 arrays) and also create another array to keep reference of the sections I will have to display.
Then, in the end, I call [myTableView reloadData] to refresh content and redraw table. Users have also the possibility to refresh content themselves. For that I'm using a pull-to-refresh mechanism just like Facebook app.
Refreshing content using pull-to-refresh also ends up calling [myTableView reloadData]. I'm experiencing a nasty crash when I refresh my content and the table needs to change its lay out (e.g: the table currently holds all 3 sections and after I reloadData it will show only one section).
I used the debugger to track down the issue. Here is what I found:
- User pulls down the entire table for like 50 pixels and then releases
- An animation begins that brings the table "up" to accommodate the 320x50 view displaying a "Loading" message + a UIActivityIndicatorView animated on TOP of the table
- Refresh is triggered, I make a new webservice request in a background task, get new data, parse, update arrays, finally on main thread perform reloadData
- In the meantime, while refresh is doing all that I noticed that UITableView STILL sends delegate messages to :
- tableView:viewForHeaderInSection:
- tableView:cellForRowAtIndexPath:
I figure this happens because of the pull-to-refresh animation that "moves" the table up and reveals new cells/headers, thus calling the above.
The problem is that this causes a horrible crash because the arrays that hold data for sections and the sections array itself are modified concurrently it seems.
When this happens, the delegate methods probably use the update/not-yet-updated array of items/sections in the wrong context. That is, before
- numberOfSectionsInTableView:
- tableView: numberOfRowsInSection:
ever have a chance to update the new structure properly for the updated arrays.
I know it's long story without any code sample, but I figured that if I already know the problem (at least I hope I do) and if someone sees my point, maybe that someone can point me in the right direction to correct this synchronization issue.
Thanks for reading!
LATER EDIT:
I found where the issue was. My pull-to-refresh mechanism uses a stopLoading callback that looks like this:
- (void)stopLoading
{ isLoading = NO;
// Hide the header
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDelegate:self];
[UIView setAnimationDuration:0.3];
[UIView setAnimationDidStopSelector:@selector(stopLoadingComplete:finished:context:)];
self.myTableView.contentInset = UIEdgeInsetsZero;
[refreshArrow layer].transform = CATransform3DMakeRotation(M_PI * 2, 0, 0, 1);
[UIView commitAnimations];
}
At some point I reset the contentInset for my table setting it at UIEdgeInsetsZero. This and the fact that after background refresh returning on main thread I was calling stopLoading BEFORE reloadData - triggered the wrong delegate callbacks in the wrong moment.
So it was a timing issue between a change to my table's contentInset and reloadData.