views:

99

answers:

2

I'm trying to add a progress meter, or other "I'm busy right now" notification to my view hierarchy right before doing some intense computation that will block the UI. My code looks some thing like:

//create view
[currentTopView addSubView:imBusyView];

//some initialization for the intense computation
[computation startComputing];

Unfortunately, my progress meter doesn't display until after the computation completes. It appears like the views aren't re-drawn until the run loop completes. I'm pretty sure that setNeedsDisplay and setNeedsLayout will also wait until the run loop completes.

How do I get the view to display immediately?

A: 

If you are doing heavy calculations maybe spawning a new thread is a good idea.

Here I have an activityIndicator displayed and starts a large XMLParse operation in a background thread:

- (void) setSearchParser {
 activityIndicator = [[ActivityIndicatorView alloc] initWithActivity];
 [self.view addSubview:activityIndicator];
 [NSThread detachNewThreadSelector:@selector(getSearchResults:) toTarget:self withObject:[searchParser retain]];
}

then the getSearchResults method:

- (void) getSearchResults: (SearchResultParser *) parser {
 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 [parser startParser];
 [self performSelectorOnMainThread:@selector(searchResultsReady:) withObject:[parser data] waitUntilDone:NO];
 [pool release];
}

So firstly make a new thread:

[NSThread detachNewThreadSelector:@selector(getSearchResults:) toTarget:self withObject:[searchParser retain]];

this means that all code inside the getSearchResults will be executed on a different thread. getSearchResults also get's passed a parameter of "searchParser" thats a large object that just needs startParse called on it to begin.

This is done in getSearchResults. When the [parser startParser] is done, the results is passed back to the main thread method called "searchResultsReady" and the threads autorelease pool is released.

All the time it took from my parser began to it had finished, a gray view covered the screen an an activityIndicator ran.

You can have the small activityIndicator class I wrote:

-(id) initWithActivity {
 [self initWithFrame:[self bounds]];
 [self setBackgroundColor:[UIColor blackColor]];
 [self setAlpha:0.8];
 activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
 activityView.center = CGPointMake(160, 240); 
 [self addSubview:activityView ];
 [activityView startAnimating];
 return self; 
}

- (void) dealloc {
 [activityView release];
 [super dealloc];
}

Hope it helps you out, even though threads seems a bit confusing, they can help to make the UI not freeze up, which is especially important on the iPhone.

RickiG
Thanks Brad.I just can't seem to get Objective C code to format correctly:)
RickiG
Thank you for the lengthy response. For my case the user will be expecting the UI to be blocked.
SooDesuNe
+1  A: 

Redrawing only occurs when your code returns control to the run loop. So the easiest way would be for you to schedule the startComputing call with a zero delay. That way, it will be executed during the next run loop iteration (right after redrawing):

[computation performSelector:@selector(startComputing) withObject:nil afterDelay:0.0];

Be aware, though, that unless you do your computation in another thread you will not be able to update the UI during the computation.

Ole Begemann