2012年8月更新:
iOS 5及更高版本引入了使用完成块(completion blocks)进行模态视图动画完成后的操作的更安全的API:
[self presentViewController:myModalVC animated:YES completion:^{}]
[self dismissViewControllerAnimated:YES completion:^{}]
2012年8月之前的答案:
当我快速地关闭模态框一并打开模态框二时,我遇到了类似的问题。有时候模态框二会在模态框一被关闭后显示,有时候模态框二则根本不会出现,这让我很难过。
看起来像是竞争条件的问题...
在调用呈现模态框二的方法 showModalTwo
之前加入1秒钟的延迟可以解决问题,每次关闭模态框一后都能够成功显示模态框二:
- (void)didDismissModalOne {
[self performSelector:@selector(showModalTwo:) withObject:someNumber afterDelay:1.0f];
}
经过确认,发现在关闭模态框1和呈现模态框2之间存在某种竞争条件。然而在调用者上设置延迟是不优雅的,并不能保证竞争条件不会在其他情况下重新出现。
问题
原来,UIViewController
有一个公共属性modalViewController
,当调用presentModalViewController:animated:
时会被设置,当调用dismissModalViewControllerAnimated:
时会被解除设置。但问题是这个属性不会同步被解除设置,所以可能会在以下方式中,在移除旧的modalViewController
值与建立新值之间创建一个竞赛:
- 呈现模态框1。现在
myViewController.modalViewController
指向模态框1
- 关闭模态框1。后台进程开始拆除
myViewController.modalViewController
,但myViewController.modalViewController
仍指向模态框1
- 呈现模态框2,
myViewController.modalViewController
现在指向模态框2
- 系统回调触发,将
myViewController.modalViewController
设置为nil
,这打断了模态框2动画的过程,结果用户永远看不到它。
竞争条件始于步骤2,并在步骤4中显现。
解决方案
我的解决方案是在呈现模态框2的方法上设置一个守卫条件,以确保myViewControoler.modalViewController
在尝试呈现模态框2之前为nil
。
-(void)showModalTwo:(NSNumber *)aParameter {
if (self.modalViewController) {
[self performSelector:@selector(showModalTwo:)
withObject:aParameter
afterDelay:0.1f];
return;
}
}
非常顺利地解决了问题。一个更优雅的解决方案可能包括超时。
后续
我真的不喜欢这个解决方案中的轮询部分。在这个问题的被接受的答案中,@Nimrod建议你可以安全地从模态视图一的viewDidDisappear:
方法开始呈现模态视图二。我喜欢这种事件驱动的方法,但在我的用例中完全实现后,我确认使用viewDidDisappear:
内部的回调呈现模态视图二仍然存在竞争条件。唯一确定模态视图二将被显示的方式是在父视图控制器中轮询,直到你绝对确定self.modalViewController
为nil
。只有在这种情况下,弹出模态视图二才是“安全”的。