I have a tableView that loads an XML feed as follows:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if ([stories count] == 0) {
Starting a new thread can be overkilling and a source of complexity if you want to do things that are supposed to start on the main thread.
In my own code, I need to start a MailComposer by pushing a button but it can take some time to appear and I want to make sure the UIActivityIndicator is spinning meanwhile.
This is what I do :
-(void)submit_Clicked:(id)event { [self.spinner startAnimating]; [self performSelector:@selector(displayComposerSheet) withObject:nil afterDelay:0]; }
It will queue displayComposerSheet instead of executing it straight away. Enough for the spinner to start animating !
I typically implement an NSTimer that will call my spinner method, which I fire off right before I go into doing the heavy work (the work that will typically block the main thread).
The NSTimer fires and my spinner method is called. When the main work is finished, I disable the spinner.
Code for that is like:
IBOutlet UIActiviyIndicatorView *loginIndicator;
{
...
[loginIndicator startAnimating];
[NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(executeAuthenticationRequest)
userInfo:nil repeats:NO];
...
}
- (void) executeAuthenticationRequest
{
/* Simulate doing a network call. */
sleep(3);
[loginIndicator stopAnimating];
...
}
You can also do:
IBOutlet NSProgressIndicator *pIndicator;
Start:
[pIndicator startAnimation:self];
[pIndicator setHidden:NO];
And Stop:
[pIndicator stopAnimation:self];
[pIndicator setHidden:YES];
Here's the problem: NSXMLParser
is a synchronous API. That means that as soon as you call parse
on your NSXMLParser
, that thread is going to be totally stuck parsing xml, which means no UI updates.
Here's how I usually solve this:
- (void) startThingsUp {
//put the spinner onto the screen
//start the spinner animating
NSString *path = @"http://myurl.com/file.xml";
[self performSelectorInBackground:@selector(parseXMLFileAtURL:) withObject:path];
}
- (void) parseXMLFileAtURL:(NSString *)path {
//do stuff
[xmlParser parse];
[self performSelectorOnMainThread:@selector(doneParsing) withObject:nil waitUntilDone:NO];
}
- (void) doneParsing {
//stop the spinner
//remove it from the screen
}
I've used this method many times, and it works beautifully.
In Cocoa (and most other app frameworks) the user interface is updated by the main thread. When you manipulate views, they are typically not redrawn until control returns to the run loop and the screen is updated.
Because you are parsing the XML in the main thread, you are not allowing the screen to update, and that is why your activity indicator is not appearing.
You should be able to fix it by doing the following:
In viewDidAppear, show/animate the spinner and then call
[self performSelector:@selector(myXMLParsingMethod) withObject:nil afterDelay:0];
In myXMLParsingMethod, parse your XML, then hide/stop the spinner.
This way, control will return to the run loop before parsing begins, to allow the spinner to begin animating.