maxdev
maxdev

Reputation: 2576

UIView animateWithDuration makes app hang when called from dispatch_async block

To start off, I have a protocol looking like this:

@protocol CryptoDelegate <NSObject>
- (void)cryptoManagerFinishedEncryption;
@end

Theres a singleton named CryptoManager that has the following function:

- (void)startEncryption:(NSURL *)path withDelegate:(id<CryptoDelegate>)delegate {
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // ... some stuff is happening here
        [delegate cryptoManagerFinishedEncryption];
    });
}

Then I have a self-made MultiPopup that can display panels (which must extend MultiPopupPanel to store i.a. a pointer to the popup itself) and switches between these panels using the following code:

- (void)switchTo:(MultiPopupPanel*)panel {
    if(self.currentPanel != panel) {
        MultiPopupPanel* oldPanel = self.currentPanel;
        self.currentPanel = panel;
        self.currentPanel.alpha = 0;
        self.currentPanel.hidden = NO;

        if(oldPanel) {
            NSLog(@"Before animation");
            [UIView animateWithDuration:0.2f animations:^{
                oldPanel.alpha = 0;
            } completion:^(BOOL finished) {
                NSLog(@"After animation");
                oldPanel.hidden = YES;
                [UIView animateWithDuration:0.2f animations:^{
                    self.currentPanel.alpha = 1;
                }];
            }];
        } else {
            [UIView animateWithDuration:0.2f animations:^{
                self.currentPanel.alpha = 1;
            }];
        }
    }
}

Now I have a panel that extends MultiPopupPanel and implements the protocol CryptoDelegate. Within its cryptoManagerFinishedEncryption implementation I'm calling my switchTo function that should then switch to another panel.

This is where the application hangs: the message "Before animation" is output immediately. Then the app hangs for 15-20 seconds, then the animation happens, the "After animation" message is output and the new panel shows up.

This only happens when I'm using the +[UIView animateWithDuration...] from within another thread (through the dispatch_async). If I call the delegate from without the dispatch_async (= in the main thread) everything goes fine.

Why does this happen? Is it allowed to call animations from async blocks? Any help is greatly appreciated.

Upvotes: 1

Views: 891

Answers (2)

Wain
Wain

Reputation: 119041

You must always call UI related code on the main thread. Not doing so is forbidden and will lead to unexpected results (and often exceptions).

When calling your delegate method you should again use dispatch_async with the dispatch_get_main_queue so that the delegate code will run on the main thread and you can update your UI.

See also these Apple docs for more specific information.

Upvotes: 3

Hashmat Khalil
Hashmat Khalil

Reputation: 1816

any changes to GUI elements as such as animation, size and content should happen on the main thread, otherwise it will have weird effects.

Upvotes: 1

Related Questions