开发者

UIView animation blocked by busy main thread

开发者 https://www.devze.com 2023-02-16 06:33 出处:网络
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 followi

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.

开发者_如何学JAVA

My 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消