最近在项目中遇到一个问题, 当一个提示页面是用present弹出并且带动画时,一个个分别弹出没有问题.但是当需要同时弹出页面并且一个叠一个时就会导致presentViewController丢失页面,原因是当上一个页面弹出还未执行完成的时候,下一个页面present就无法真正的弹出.
这边我写一下我的解决方案
1.首先创建一个类继承UINavigationController,在项目中这个类是我的window.rootViewController.
#import <UIKit/UIKit.h>
@interface RootNavigationVController : UINavigationController
@property (nonatomic, strong) dispatch_semaphore_t signal;
@end
如以上代码所示为RootNavigationVController 添加dispatch_semaphore_t 后续我们使用信号量的方式来控制presentViewController的弹出.
2. 重写- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)())completion 方法:
- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)())completion {
//初始化信号量,最高并发执行 1
if (!self.signal) {
self.signal = dispatch_semaphore_create(1);
}
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//当present被调用时, wait -1,此时信号量为0,其他执行等待
dispatch_semaphore_wait(self.signal, dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
dispatch_async(dispatch_get_main_queue(), ^{
//判断是否是modal视图
if (self.presentedViewController) {
UIViewController *vc = self.presentedViewController;
while (vc.presentedViewController) {
vc = vc.presentedViewController;
}
//取栈顶的modal视图来执行
[vc presentViewController:viewControllerToPresent animated:flag completion:^{
dispatch_semaphore_signal(self.signal); //弹窗完成信号量 +1 允许下一次弹窗任务执行
if (completion) {
completion();
}
}];
} else { //同上
[super presentViewController:viewControllerToPresent animated:flag completion:^{
dispatch_semaphore_signal(self.signal);
if (completion) {
completion();
}
}];
}
});
});
}
这边主要就是使用信号量在进入presentViewContrller的时候对线程进行阻塞, 在并发的时候让下一个执行等待.然后completion 的block里面弹窗完成,将信号量+1,允许下一个页面执行.同时判断上一个页面类型使用不同方法进行弹窗. 这边我dispatch_semaphore_wait 的timeout设置是5秒,不同业务根据需求设置即可.
3.在业务中使用时:
[self.navigationController presentViewController:vc animated:YES completion:nil];
这样不会影响原有其他地方的presentViewController方法,使用上也没有其他变化.