iOS14+版本选择导航历史栈项时App崩溃问题求助
问题概述
在iOS14及以上版本中,处于Notification Detail界面时长按返回按钮调出历史栈,选择User Screen时App崩溃。界面层级如下:
Main Screen (UINavigationController) | V User Screen (UIViewController) | V Notifications Screen (UIViewController) | V Notifications Detail Screen (UIViewController)
崩溃报错信息:
*** Assertion failure in -[UINavigationController _setViewControllers:transition:animated:operation:], UINavigationController.m:1257
main : *** an exception [UINavigationControllers are not allowed in a navigation controller!
(
0 CoreFoundation 0x00007ff8004288ab __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007ff80004dba3 objc_exception_throw + 48
2 Foundation 0x00007ff800b870cc _userInfoForFileAndLine + 0
3 UIKitCore 0x000000010c5c1813 __76-[UINavigationController _setViewControllers:transition:animated:operation:]_block_invoke + 255
)
核心原因
崩溃提示UINavigationControllers are not allowed in a navigation controller!明确说明:系统处理历史栈跳转时,错误地将作为根容器的Main Screen(UINavigationController)当成普通视图控制器,试图将其加入当前导航栈,违反了导航控制器不能嵌套的规则。这是iOS14+返回历史栈功能的适配偏差,跨层级跳转时系统对栈结构的解析出现错误。
解决方法
- 检查导航栈结构:确认User Screen是直接由Main Screen导航控制器push或作为根视图控制器加载的,没有被意外包裹在另一个UINavigationController实例中。
- 拦截历史栈跳转逻辑:重写当前导航控制器的
setViewControllers:animated:方法,过滤掉导航控制器类型的元素,确保最终设置的视图控制器数组中只有普通UIViewController实例。示例代码:- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated { NSMutableArray *filteredVCs = [NSMutableArray array]; for (UIViewController *vc in viewControllers) { if (![vc isKindOfClass:[UINavigationController class]]) { [filteredVCs addObject:vc]; } } [super setViewControllers:filteredVCs animated:animated]; } - 规范页面跳转流程:确保所有页面跳转都通过同一个Main Screen导航控制器执行push/pop操作,避免在中间环节(如从User Screen到Notifications Screen)初始化新的导航控制器并跳转。
- 适配iOS14+返回栈:利用
UINavigationControllerDelegate的navigationController:willShowViewController:animated:方法,在页面即将显示时检查栈结构,若发现导航控制器嵌套情况,手动调整栈内容。
内容的提问来源于stack exchange,提问作者Rahul A.

