You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS设备旋转时UINavigationController意外弹出问题求助

问题:UISplitViewController折叠模式下旋转导致导航控制器意外Pop(iOS 16,iPhone 11/Xr专属)

问题细节

  • 影响设备:仅iPhone 11、iPhone Xr的模拟器及真机,X、XS、11Pro、12、13Pro、14Pro等机型无法复现
  • 系统版本:所有测试基于iOS 16
  • 触发场景:当折叠模式下的UISplitViewController主控制器为UINavigationController时,设备旋转会导致导航控制器意外执行pop操作——该pop并非正常用户触发,且isMovingFromParent不会被设为true,仅会将导航栈返回到上一控制器
  • SplitVC配置:采用经典界面(未使用iOS14+列布局)
  • 断点验证:已在[UINavigationController popViewControllerAnimated:]设置断点确认问题,触发时的调用栈如下:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 6.1
    frame #0: 0x0000000108e28434 UIKitCore`-[UINavigationController popViewControllerAnimated:]
    frame #1: 0x0000000108e2faa4 UIKitCore`-[UINavigationController _separateViewControllersAfterAndIncludingViewController:forSplitViewController:] + 284
    frame #2: 0x0000000108e2699c UIKitCore`-[UINavigationController separateSecondaryViewControllerForSplitViewController:] + 24
  * frame #3: 0x0000000108ea36fc UIKitCore`-[UISplitViewControllerPanelImpl panelController:unspecifiedStyleSeparateSecondaryViewControllerFromPrimaryViewController:] + 428
    frame #4: 0x0000000108ea2a0c UIKitCore`-[UISplitViewControllerPanelImpl panelController:separateSecondaryViewControllerFromPrimaryViewController:] + 344
    frame #5: 0x0000000108e6edac UIKitCore`__54-[UIPanelController _expandWithTransitionCoordinator:]_block_invoke_4 + 84
    frame #6: 0x0000000108e70da8 UIKitCore`+[UIPanelController _withDisabledAppearanceTransitions:forVisibleDescendantsOf:perform:] + 436
    frame #7: 0x0000000108e707e0 UIKitCore`-[UIPanelController _withDisabledAppearanceTransitionsPerform:] + 132
    frame #8: 0x0000000108e6ed38 UIKitCore`__54-[UIPanelController _expandWithTransitionCoordinator:]_block_invoke_3 + 104
    frame #9: 0x0000000108ec736c UIKitCore`+[UIViewController _performWithoutDeferringTransitionsAllowingAnimation:actions:] + 132
    frame #10: 0x0000000108e6ecb0 UIKitCore`__54-[UIPanelController _expandWithTransitionCoordinator:]_block_invoke_2 + 112
    frame #11: 0x00000001099ec138 UIKitCore`+[UIView(Animation) performWithoutAnimation:] + 68
    frame #12: 0x0000000108e6ec1c UIKitCore`__54-[UIPanelController _expandWithTransitionCoordinator:]_block_invoke + 152
    frame #13: 0x0000000108edfb90 UIKitCore`-[_UIViewControllerTransitionCoordinator _applyBlocks:releaseBlocks:] + 228
    frame #14: 0x0000000108edd29c UIKitCore`-[_UIViewControllerTransitionContext __runAlongsideAnimations] + 212
    frame #15: 0x00000001099ed584 UIKitCore`+[UIView _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 500
    frame #16: 0x00000001099ed96c UIKitCore`+[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:] + 44
    frame #17: 0x0000000108ef226c UIKitCore`__58-[_UIWindowRotationAnimationController animateTransition:]_block_invoke_2 + 220
    frame #18: 0x00000001099f0bdc UIKitCore`+[UIView _performBlockDelayingTriggeringResponderEvents:forScene:] + 168
    frame #19: 0x0000000108ef208c UIKitCore`__58-[_UIWindowRotationAnimationController animateTransition:]_block_invoke + 140
    frame #20: 0x00000001099ee6b0 UIKitCore`+[UIView(UIViewAnimationWithBlocksPrivate) _modifyAnimationsWithPreferredFrameRateRange:updateReason:animations:] + 164
    frame #21: 0x00000001099ed584 UIKitCore`+[UIView _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 500
    frame #22: 0x00000001099ed96c UIKitCore`+[UIView(UIViewAnimationWithBlocks) animateWithDuration:delay:options:animations:completion:] + 44
    frame #23: 0x0000000108ef1ef4 UIKitCore`-[_UIWindowRotationAnimationController animateTransition:] + 448
    frame #24: 0x0000000109591cfc UIKitCore`-[UIWindow _rotateToBounds:withAnimator:transitionContext:] + 500
    frame #25: 0x0000000109594154 UIKitCore`-[UIWindow _rotateWindowToOrientation:updateStatusBar:duration:skipCallbacks:] + 1092
    frame #26: 0x0000000109594594 UIKitCore`-[UIWindow _setRotatableClient:toOrientation:updateStatusBar:duration:force:isRotating:] + 320
    frame #27: 0x0000000109590f80 UIKitCore`-[UIWindow _internal_setRotatableViewOrientation:updateStatusBar:duration:force:] + 104
    frame #28: 0x0000000109592c18 UIKitCore`__57-[UIWindow _updateToInterfaceOrientation:duration:force:]_block_invoke + 188
    frame #29: 0x000000010959296c UIKitCore`-[UIWindow _updateToInterfaceOrientation:duration:force:] + 844
    frame #30: 0x0000000108cabc04 UIKitCore`-[_UIScenefbsSceneBasedMetricsCalculator _updateMetricsOnWindows:animated:] + 1004
    frame #31: 0x0000000109788588 UIKitCore`-[UIWindowScene _computeMetricsForWindows:animated:] + 84
    frame #32: 0x0000000109787b68 UIKitCore`__55-[UIWindowScene _computeMetrics:withTransitionContext:]_block_invoke + 100
    frame #33: 0x0000000109787ca8 UIKitCore`-[UIWindowScene _computeTraitCollectionAndCoordinateSpaceForcingDelegateCallback:withAction:] + 288
    frame #34: 0x0000000109787af8 UIKitCore`-[UIWindowScene _computeMetrics:withTransitionContext:] + 76
    frame #35: 0x0000000109784ca8 UIKitCore`-[UIWindowScene _computeMetricsAndCrossFadeInLiveResize:withTransitionContext:] + 204
    frame #36: 0x00000001099ed584 UIKitCore`+[UIView _setupAnimationWithDuration:delay:view:options:factory:animations:start:animationStateGenerator:completion:] + 500
    frame #37: 0x00000001099ed7b0 UIKitCore`+[UIView(UIViewAnimationWithBlocks) _animateWithDuration:delay:options:factory:animations:completion:] + 48
    frame #38: 0x000000010908d618 UIKitCore`+[BSAnimationSettings(UIKit) tryAnimatingWithSettings:fromCurrentState:actions:completion:] + 468
    frame #39: 0x000000010918d0d4 UIKitCore`_UISceneSettingsDiffActionPerformChangesWithTransitionContextAndCompletion + 196
    frame #40: 0x0000000108cb0aa8 UIKitCore`-[_UIWindowSceneGeometrySettingsDiffAction _updateSceneGeometryWithSettingObserverContext:windowScene:transitionContext:] + 764
    frame #41: 0x0000000108cb0718 UIKitCore`-[_UIWindowSceneGeometrySettingsDiffAction _performActionsForUIScene:withUpdatedFBSScene:settingsDiff:fromSettings:transitionContext:lifecycleActionType:] + 1084
    frame #42: 0x0000000108b20b3c UIKitCore`__64-[UIScene scene:didUpdateWithDiff:transitionContext:completion:]_block_invoke.196 + 552
    frame #43: 0x0000000108b1f93c UIKitCore`-[UIScene _emitSceneSettingsUpdateResponseForCompletion:afterSceneUpdateWork:] + 208
    frame #44: 0x0000000108b207f8 UIKitCore`-[UIScene scene:didUpdateWithDiff:transitionContext:completion:] + 220
    frame #45: 0x00000001090b35d8 UIKitCore`-[UIApplicationSceneClientAgent scene:handleEvent:withCompletion:] + 308
    frame #46: 0x0000000184e593d4 FrontBoardServices`-[FBSScene updater:didUpdateSettings:withDiff:transitionContext:completion:] + 392
    frame #47: 0x0000000184e810d0 FrontBoardServices`__94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke_2 + 124
    frame #48: 0x0000000184e64704 FrontBoardServices`-[FBSWorkspace _calloutQueue_executeCalloutFromSource:withBlock:] + 160
    frame #49: 0x0000000184e8101c FrontBoardServices`__94-[FBSWorkspaceScenesClient _queue_updateScene:withSettings:diff:transitionContext:completion:]_block_invoke + 312
    frame #50: 0x0000000105ccdd50 libdispatch.dylib`_dispatch_client_callout + 16
    frame #51: 0x0000000105cd1968 libdispatch.dylib`_dispatch_block_invoke_direct + 392
    frame #52: 0x0000000184e9ef14 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 44
    frame #53: 0x0000000184e9ee08 FrontBoardServices`-[FBSSerialQueue _targetQueue_performNextIfPossible] + 176
    frame #54: 0x0000000184e9ef48 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 24
    frame #55: 0x000000018039ac6c CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24
    frame #56: 0x000000018039abb4 CoreFoundation`__CFRunLoopDoSource0 + 172
    frame #57: 0x000000018039a324 CoreFoundation`__CFRunLoopDoSources0 + 232
    frame #58: 0x0000000180394958 CoreFoundation`__CFRunLoopRun + 748
    frame #59: 0x0000000180394254 CoreFoundation`CFRunLoopRunSpecific + 584
    frame #60: 0x0000000188eb7c9c GraphicsServices`GSEventRunModal + 160
    frame #61: 0x0000000109552ff0 UIKitCore`-[UIApplication _run] + 868
    frame #62: 0x0000000109556f3c UIKitCore`UIApplicationMain + 124
    frame #63: 0x000000010412ac20 aast`main at AppDelegate.swift:4:7
    frame #64: 0x00000001042bd514 dyld_sim`start_sim + 20
    frame #65: 0x000000010444df28 dyld`start + 2236

开发者反馈及解决办法

开发者反馈情况

确实有不少开发者在技术社区反馈过该问题,属于iOS 16针对iPhone 11/Xr这类特定屏幕尺寸机型的UIKit内部bug,根源是SplitViewController在旋转时错误触发了导航控制器的分离逻辑,导致意外pop。

可行解决办法

  • 自定义导航控制器拦截异常pop
    继承UINavigationController,通过检查调用栈识别SplitVC触发的异常pop并拦截:

    class SafeNavigationController: UINavigationController {
        override func popViewController(animated: Bool) -> UIViewController? {
            // 检查调用栈是否包含SplitVC相关的私有方法,判断是否为异常触发
            let isSplitInitiated = Thread.callStackSymbols.contains {
                $0.contains("_separateViewControllersAfterAndIncludingViewController:forSplitViewController:")
            }
            if isSplitInitiated && viewControllers.count > 1 {
                // 拦截异常pop,返回nil
                return nil
            }
            return super.popViewController(animated: animated)
        }
    }
    

    注意:调用栈检查依赖私有方法符号,需做好兼容性测试。

  • 通过SplitVC代理阻止过渡中的分离操作
    实现UISplitViewControllerDelegate,在旋转过渡中返回nil阻止SecondaryViewController分离:

    extension YourSplitVC: UISplitViewControllerDelegate {
        func splitViewController(_ svc: UISplitViewController, separateSecondaryViewControllerFrom primaryViewController: UIViewController) -> UIViewController? {
            // 处于旋转过渡时阻止分离
            if svc.isTransitioning {
                return nil
            }
            // 正常场景返回SecondaryVC逻辑不变
            return (primaryViewController as? UINavigationController)?.topViewController?.presentedViewController
        }
    }
    
  • 升级系统版本
    Apple在iOS 16.4及后续版本中修复了该类SplitVC布局bug,若允许用户升级系统,可优先选择此方案。

内容的提问来源于stack exchange,提问作者Rudedog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 23:37:21