iOS开发:如何在Scene Delegate中获取当前模态UIViewController?
解决方法
1. 适配iOS 13+多Scene架构,修正顶层VC获取逻辑
iOS 13及以后采用多Scene架构,原有的UIApplication.shared.keyWindow方式无法保证获取到当前活跃窗口。需从当前Scene的window入手,同时优化递归获取逻辑,避免强制解包导致的异常:
func getTopViewController(from rootVC: UIViewController?) -> UIViewController? { guard let rootVC = rootVC else { return nil } // 处理TabBar控制器 if let tabBarVC = rootVC as? UITabBarController { return getTopViewController(from: tabBarVC.selectedViewController) } // 处理导航控制器 if let navVC = rootVC as? UINavigationController { return getTopViewController(from: navVC.visibleViewController) } // 递归获取最上层模态控制器 if let presentedVC = rootVC.presentedViewController { return getTopViewController(from: presentedVC) } return rootVC }
在SceneDelegate的深链接处理方法(如scene(_:openURLContexts:))中调用:
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) { // 获取当前Scene的活跃窗口 guard let windowScene = scene as? UIWindowScene, let window = windowScene.windows.first, let rootVC = window.rootViewController else { return } let topVC = getTopViewController(from: rootVC) if let loginVC = topVC as? LoginViewController { // 在此处对Login控制器执行编程操作 print("成功获取Login控制器:\(loginVC)") } }
2. 排查模态弹出时机问题
若调用时presentedViewController返回nil,可能是深链接触发时Login控制器的present动画尚未完成。可通过延迟获取或通知机制解决:
- 延迟获取:在深链接处理逻辑中加入短暂延迟,确保模态控制器完全弹出:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { let topVC = getTopViewController(from: rootVC) // 后续操作 }
- 通知机制:在Login控制器的
viewDidAppear(_:)中发送通知,让SceneDelegate在合适时机获取引用:
// LoginViewController.swift override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) NotificationCenter.default.post(name: NSNotification.Name("LoginVCDidPresent"), object: self) } // SceneDelegate.swift func sceneDidBecomeActive(_ scene: UIScene) { NotificationCenter.default.addObserver(self, selector: #selector(handleLoginPresented(_:)), name: NSNotification.Name("LoginVCDidPresent"), object: nil) } @objc func handleLoginPresented(_ notification: Notification) { if let loginVC = notification.object as? LoginViewController { // 操作Login控制器 } } func sceneWillResignActive(_ scene: UIScene) { NotificationCenter.default.removeObserver(self) }
3. 检查Dashboard的present逻辑
确认Dashboard使用标准系统方法弹出Login控制器,且模态样式设置正确:
// Dashboard中的present代码示例 let loginVC = LoginViewController() loginVC.modalPresentationStyle = .fullScreen // 确保为全屏模态 present(loginVC, animated: true)
内容的提问来源于stack exchange,提问作者thesuffering
相关产品推荐
相关产品推荐

