UIView animateWithDuration 使应用程序在从 dispatch_async 块调用时挂起
UIView animateWithDuration makes app hang when called from dispatch_async block
首先,我有一个如下所示的协议:
@protocol CryptoDelegate <NSObject>
- (void)cryptoManagerFinishedEncryption;
@end
有一个名为 CryptoManager
的单例,具有以下功能:
- (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];
});
}
然后我有一个自制的MultiPopup
可以显示面板(它必须扩展MultiPopupPanel
来存储i.a。一个指向弹出窗口本身的指针)并在这些面板之间切换使用以下代码:
- (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;
}];
}
}
}
现在我有一个扩展 MultiPopupPanel
并实现协议 CryptoDelegate
的面板。在它的 cryptoManagerFinishedEncryption
实现中,我正在调用我的 switchTo
函数,然后它应该切换到另一个面板。
这是应用程序挂起的地方:立即输出消息 "Before animation"。然后应用挂起 15-20 秒,然后出现动画,输出 "After animation" 消息并显示新面板。
只有当我在另一个线程(通过 dispatch_async)中使用 +[UIView animateWithDuration...]
时才会发生这种情况。如果我在没有 dispatch_async
的情况下调用委托(= 在主线程中),一切正常。
为什么会这样?是否允许从异步块调用动画?非常感谢任何帮助。
任何对 GUI 元素(例如动画、大小和内容)的更改都应该在主线程中进行,否则会产生奇怪的效果。
您必须始终在主线程上调用 UI 相关代码。不这样做是被禁止的,并且会导致意想不到的结果(通常是例外)。
当调用你的委托方法时,你应该再次使用 dispatch_async
和 dispatch_get_main_queue
,这样委托代码将 运行 在主线程上,你可以更新你的 UI.
另请参阅 these Apple docs 了解更多具体信息。
首先,我有一个如下所示的协议:
@protocol CryptoDelegate <NSObject>
- (void)cryptoManagerFinishedEncryption;
@end
有一个名为 CryptoManager
的单例,具有以下功能:
- (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];
});
}
然后我有一个自制的MultiPopup
可以显示面板(它必须扩展MultiPopupPanel
来存储i.a。一个指向弹出窗口本身的指针)并在这些面板之间切换使用以下代码:
- (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;
}];
}
}
}
现在我有一个扩展 MultiPopupPanel
并实现协议 CryptoDelegate
的面板。在它的 cryptoManagerFinishedEncryption
实现中,我正在调用我的 switchTo
函数,然后它应该切换到另一个面板。
这是应用程序挂起的地方:立即输出消息 "Before animation"。然后应用挂起 15-20 秒,然后出现动画,输出 "After animation" 消息并显示新面板。
只有当我在另一个线程(通过 dispatch_async)中使用 +[UIView animateWithDuration...]
时才会发生这种情况。如果我在没有 dispatch_async
的情况下调用委托(= 在主线程中),一切正常。
为什么会这样?是否允许从异步块调用动画?非常感谢任何帮助。
任何对 GUI 元素(例如动画、大小和内容)的更改都应该在主线程中进行,否则会产生奇怪的效果。
您必须始终在主线程上调用 UI 相关代码。不这样做是被禁止的,并且会导致意想不到的结果(通常是例外)。
当调用你的委托方法时,你应该再次使用 dispatch_async
和 dispatch_get_main_queue
,这样委托代码将 运行 在主线程上,你可以更新你的 UI.
另请参阅 these Apple docs 了解更多具体信息。