iOS 17 iPad应用模态弹窗崩溃求助(含keyWindow废弃问题)
问题分析与修复方案
核心崩溃原因
- 自呈现错误:快速连续开关弹窗时,前一个
VolumeVC未完全 dismiss,原topViewController方法错误将其识别为顶层控制器,导致用VolumeVC自身呈现新实例,触发Application tried to present modal view controller on itself崩溃。 - 顶层控制器获取逻辑缺陷:原方法仅遍历
presentedViewController,未处理UITabBarController、UISplitViewController这类容器控制器的嵌套结构,iOS17视图生命周期变化下易拿到错误控制器。 - sourceView为空问题:替换
keyWindow后未正确获取活跃窗口,或传入的view已处于不可用状态,导致UIPopoverPresentationController的sourceView为nil。
具体修复步骤
1. 修复顶层控制器获取逻辑
替换原方法,处理容器控制器嵌套,排除正在dismiss的控制器:
+ (UIViewController*)topViewController { UIWindow *window = [self getActiveWindow]; if (!window) return nil; UIViewController *topVC = window.rootViewController; while (true) { // 处理UITabBarController if ([topVC isKindOfClass:[UITabBarController class]]) { topVC = [(UITabBarController *)topVC selectedViewController]; } // 处理UISplitViewController else if ([topVC isKindOfClass:[UISplitViewController class]]) { topVC = [(UISplitViewController *)topVC viewControllers].lastObject; } // 处理presentedViewController,跳过正在dismiss的实例 if (topVC.presentedViewController && !topVC.presentedViewController.isBeingDismissed) { topVC = topVC.presentedViewController; } else { break; } } return topVC; } // 适配iOS13+的活跃窗口获取 + (UIWindow*)getActiveWindow { UIWindow *activeWindow = nil; if (@available(iOS 13.0, *)) { for (UIWindowScene *scene in [UIApplication sharedApplication].connectedScenes) { if (scene.activationState == UISceneActivationStateForegroundActive) { activeWindow = scene.windows.firstObject; break; } } } else { activeWindow = [UIApplication sharedApplication].keyWindow; } return activeWindow; }
2. 修正弹窗展示方法的逻辑漏洞
移除冗余代码,增加安全校验:
+ (void)showDialogiPad:(UIViewController *)contentVC fromPresentingVC:(UIViewController *)presentingVC inView:(UIView *)view atFrame:(CGRect)frame withDirection:(UIPopoverArrowDirection)direction { // 基础参数校验 if (!contentVC || !presentingVC || contentVC == presentingVC) { NSLog(@"Invalid popover presentation parameters"); return; } // 校验sourceView有效性 if (!view || !view.window) { NSLog(@"Source view is not in window hierarchy"); return; } contentVC.modalPresentationStyle = UIModalPresentationPopover; UIPopoverPresentationController *popoverPC = contentVC.popoverPresentationController; popoverPC.sourceRect = frame; popoverPC.sourceView = view; popoverPC.permittedArrowDirections = direction; // 确保presentingVC处于可呈现状态 if (!presentingVC.isBeingDismissed && presentingVC.view.window) { [presentingVC presentViewController:contentVC animated:YES completion:nil]; } }
3. 调用阶段的额外防护
避免重复呈现未关闭的弹窗,可添加实例跟踪逻辑:
static VolumeVC *_currentVolumeVC; - (void)triggerVolumePopover { // 若当前弹窗未关闭,先dismiss再重新展示 if (_currentVolumeVC && _currentVolumeVC.presentingViewController) { [_currentVolumeVC dismissViewControllerAnimated:YES completion:^{ _currentVolumeVC = nil; [self triggerVolumePopover]; }]; return; } _currentVolumeVC = [[VolumeVC alloc] init]; [Utility showDialogiPad:_currentVolumeVC fromPresentingVC:[Utility topViewController] inView:self.view atFrame:self.volumeBtn.frame withDirection:UIPopoverArrowDirectionAny]; }
验证要点
- 快速连续点击弹窗开关,确认无自呈现崩溃;
- 全场景测试弹窗,确认无
non-nil sourceView错误; - 验证iOS15与iOS17版本的兼容性。
内容的提问来源于stack exchange,提问作者Silverness
相关产品推荐
相关产品推荐

