I'm building my own activity indicator like class that's supposed to fade in before a heavy operation, and fade out when the operation is complete. This is working fine, until I run into the following scenario:
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
for( int i = 0; i < 1000; i++ ) {
NSLog(@"Performing heavy operation...");
}
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
What's happening on the first line is that my loader view is alloced, subviewed and told to fade in with a standard UIView animation. However, the animation doesn't start (as shown by the setAnimationWillStartSelector:
) until after the heavy operation is complete.
Now, heavy operations on the main thread are of course to be avoided, but I still want my loader class to work no matter what programmers might throw at it.
I tried moving the loader into a separate thread and animating it from there which worked great, but led to crashes because it's not cool to manipulate views from threads other than the main thread.
开发者_如何学JAVAMy question: Is it possible to do what I want, and/or should I bother with it at all?
As an alternative to Joshua Smith's suggestion, in case being on a different thread messes with your operation, just make sure you drop out to the runloop between starting the UIView animations and starting your heavy code. E.g.
...
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
[self performSelector:@selector(performHeavyOperation) withObject:nil afterDelay:0];
}
- (void)performHeavyOperation
{
for( int i = 0; i < 1000; i++ ) {
NSLog(@"Performing heavy operation...");
}
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
}
The performSelector:withObject:afterDelay: causes the nomated selector to be scheduled on the runloop in the future. Setting a delay of 0 means it is added to the runloop to occur as soon as possible.
For various reasons, quite a lot of UIView stuff takes effect only if you allow the call stack to unwind all the way to the call stack. That's so that, e.g. if you did:
view.frame = aNewFrame;
view.someOtherPropertyThatWouldCauseAVisibleChange = anotherValue;
Then the UIView will end up redrawing itself only once, not twice.
Put your heavy operation in an NSOperationQueue, then it will not block the main thread.
@interface MyClass : NSOperation {
}
@end
@implementation MyClass
-(void) main {
for( int i = 0; i < 1000; i++ ) {
NSLog(@"Performing heavy operation...");
}
}
@end
Then, in your above code:
[[MyLoaderClass sharedInstance] displayLoaderInView:self.view];
NSOperationQueue *q = [[NSOperationQueue alloc] init];
MyClass *c = [[[MyClass alloc] init] autorelease];
[q addOperation:c];
[q waitUntilAllOperationsAreFinished];
[[MyLoaderClass sharedInstance] removeLoaderInView:self.view];
Read the docs, too, you'll need them: http://developer.apple.com/library/mac/#documentation/cocoa/reference/NSOperationQueue_class/Reference/Reference.html
NSOperationQueue's are awesome, but not exactly intuitive.
精彩评论