I have a UITableViewController that when opened displays a table of the following object:
class {
NSString *stringVal;
int value;
}
However, whenever this controller opens, I want it to download the data from the internet and display "Connecting…" in the status bar and refresh the stringVal and value of all of the objects. I do this by refreshing the array in the UITableViewController. However, to do this the UI hangs sometimes or even displays "blank" table cells until the operation has ended. I’m doing this in an NSOperationQueue to download the data, but I’m wondering if there’s a better way to refresh the data without those weird UI bugs.
EDIT:
the UI no longer displays blank cells. This was because cellForRowAtIndexPath was setting nil values for my cellText. However, it still seems somewhat laggy when tableView.reloadData is called even though I’m using NSOperationQueue.
EDIT2:
Moreover, I have two problems: 1. the scrolling prevents the UI from being updated and 2. when the scrolling does stop and the UI starts to update, it hangs a little bit. A perfect example of what I’m trying to do can be found in the native Mail app when you view a list of folders with their unread count. If you constantly scroll the tableview, the folders unread count will be updated without any hanging at all.
Based on your response in the question comments, it sounds like you are calling [tableView reloadData] from a background thread.Do not do this. UIKit methods, unless otherwise specified, always need to be called from the main thread. Failing to do so can cause no end of problems, and you are probably seeing one of them.
EDIT: I misread your comment. It sounds like you are not updating the UI from a background thread. But my comments about the architecture (i.e. why are you updating in a background thread AFTER the download has finished?).
You state that “when the data comes back from the server, I call a background operation…” This sounds backwards. Normally you would have your NSURLConnection (or whatever you are using for the download) run on the background thread so as not to block to UI, then call out to the main thread to update the data model and refresh the UI. Alternatively, use an asynchronous NSURLConnection (which manages its own background thread/queue), e.g.:
And just make sure to use [NSOperationQueue mainQueue] for the queue.
You can also use GCD, i.e., nested dispatch_async() calls (the outer to a background queue for handling a synchronous connection, the inner on the main queue to handle the connection response).
Finally, I will note that you in principle can update your data model on the background thread and just refresh the UI from the main thread. But this means that you need to take care to make your model code thread-safe, which you are likely to mess up at least a couple times. Since updating the model is probably not a time consuming step, I would just do it on the main thread too.
EDIT:
I am adding an example of how one might use GCD and synchronous requests to accomplish this. Clearly there are many ways to accomplish non-blocking URL requests, and I do not assert that this is the best one. It does, in my opinion, have the virtue of keeping all the code for processing a request in one place, making it easier to read.
The code has plenty of rough edges. For example, creating a custom dispatch queue is not generally necessary. It blindly assumes UTF-8 encoding of the returned web page. And none of the content (save the HTTP error description) is localized. But it does demonstrate how to run non-blocking requests and detect errors (both at the network and HTTP layers). Hope this is helpful.
EDIT:
Oh, one more big rough edge. The above code assumes that any HTTP status code != 200 is an error. This is not necessarily the case, but handling this is beyond the scope of this question.