Swift 5 iOS13:无卡片视图跳转Storyboard/未关联控制器的官方方案
iOS 13+ 无卡片视图的跨Storyboard/ViewController跳转方案(替代废弃的keyWindow)
我太懂你这个痛点了!iOS 13把keyWindow废弃后,登录/登出场景用旧的present跳转出现可下滑返回的卡片,或者依赖废弃API的临时方案总觉得心里不踏实对吧?下面给你讲官方推荐的正确做法,完全适配iOS 13+、Swift 5.1和Xcode 11+环境。
问题根源拆解
- iOS 13引入了UIScene多场景机制,一个App可能同时存在多个窗口,所以全局的
keyWindow被废弃了 - 用
present跳转本质是模态展示,会保留原视图控制器栈,所以用户能下滑返回,这在登录完成跳主界面、登出跳登录页的场景下完全不符合需求
官方推荐解决方案:获取当前活跃窗口并替换根控制器
核心思路是找到当前App的活跃窗口,直接设置它的rootViewController——这种方式会重置整个界面栈,不会生成可返回的卡片视图,完美解决你的场景问题。
1. 通用工具方法:获取当前活跃窗口
先写一个全局可用的工具方法,兼容iOS 13前后的系统:
import UIKit extension UIApplication { var currentActiveWindow: UIWindow? { if #available(iOS 13.0, *) { return connectedScenes .filter { $0.activationState == .foregroundActive } .compactMap { $0 as? UIWindowScene } .first?.windows .first { $0.isKeyWindow } } else { return keyWindow } } }
2. 实际跳转代码示例
不管你是在哪个ViewController里,都可以用这个方法完成跳转:
// 示例1:登录成功后跳转到主界面TabBarController let homeTabBarVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "homeVC") as! UITabBarController if let window = UIApplication.shared.currentActiveWindow { // 替换根控制器 window.rootViewController = homeTabBarVC // 可选:添加过渡动画,让跳转更自然 UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve, animations: nil, completion: nil) } // 示例2:登出后跳转到登录页 let loginVC = UIStoryboard(name: "Auth", bundle: nil).instantiateViewController(withIdentifier: "LoginVC") if let window = UIApplication.shared.currentActiveWindow { window.rootViewController = loginVC UIView.transition(with: window, duration: 0.3, options: .transitionFlipFromLeft, animations: nil, completion: nil) }
3. 基于SceneDelegate的更优雅写法
如果你的项目用了iOS 13的SceneDelegate,可以直接在里面封装跳转逻辑,调用起来更清晰:
// 在SceneDelegate.swift中添加方法 func switchToRootViewController(_ vc: UIViewController, withAnimation animation: UIView.AnimationOptions = .transitionCrossDissolve) { guard let window = self.window else { return } window.rootViewController = vc UIView.transition(with: window, duration: 0.3, options: animation, animations: nil, completion: nil) } // 在其他ViewController中调用 if let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate { let targetVC = UIStoryboard(name: "TargetStoryboard", bundle: nil).instantiateViewController(withIdentifier: "TargetVC") sceneDelegate.switchToRootViewController(targetVC) }
为什么这是官方方案?
这种方式完全遵循iOS 13的多场景设计规范,通过connectedScenes获取当前活跃场景的窗口,避免了依赖废弃API的风险,同时直接替换根控制器的方式彻底切断了原视图控制器栈,用户无法通过下滑返回,完全满足登录/登出这类场景的需求。
内容的提问来源于stack exchange,提问作者Lukas Bimba
相关产品推荐
相关产品推荐

