「keyWindow」已弃用。该怎么办?
\n\n如果你曾经写过:
\n\nguard let window = UIApplication.shared.keyWindow else { return }\n\n……那么你肯定见过Xcode的黄色警告:「'keyWindow' was deprecated in iOS 13.0」。这是Stack Overflow上最常见的问题之一(超过10万次浏览),这并不奇怪——苹果确实改变了游戏规则。
\n\n在本文中,我们将分析为什么会发生这种情况,并比较4种方法来获取应用程序窗口而不使用已弃用的API。你将了解哪种方法适用于UIKit、SwiftUI、SceneDelegate,以及何时使用每种方法。开始吧!
\n\n为什么苹果弃用了keyWindow?
\n\n在iOS 13之前,应用程序只有一个主窗口——keyWindow。一切都很简单:获取它然后使用。但随着iPadOS和多任务处理(分屏视图、侧拉)的到来,应用程序可能同时拥有多个窗口。每个窗口由其自己的UIWindowScene管理。「关键窗口」的概念变得模糊。
苹果现在建议通过场景(UIWindowScene)来获取窗口,该场景属于你的控制器或视图。
\n\n方法比较:从简单到正确
\n\n我们将介绍4种方法,从「快速消除警告」到「按苹果的方式做」。选择适合你架构的方法。
\n\n1. 快速修复:UIApplication.shared.windows.first(where:)
\n\nStack Overflow上最流行的答案是使用windows属性代替keyWindow:
if let window = UIApplication.shared.windows.first(where: { $0.isKeyWindow }) {\n // 使用 window\n}\n\n优点:
\n- \n
- 代码改动最少 \n
- 在iOS 13+上有效 \n
缺点:
\n- \n
- 在多个窗口时仍然不安全 \n
- 如果窗口尚未创建,可能返回nil \n
- 在某些上下文中,苹果仍将其标记为「已弃用」 \n
何时使用:仅用于快速原型,或者如果你的应用确实只有一个窗口(例如,没有多窗口功能的iPhone应用)。
\n\n2. 通过SceneDelegate:UIKit的正确方法
\n\n从iOS 13开始,苹果引入了UISceneDelegate。在scene(_:willConnectTo:options:)方法中,你会获得UIWindowScene,并可以保存对窗口的引用。
步骤1:在SceneDelegate.swift中声明window属性:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {\n\n var window: UIWindow?\n\n func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {\n guard let windowScene = (scene as? UIWindowScene) else { return }\n window = UIWindow(windowScene: windowScene)\n window?.rootViewController = ViewController()\n window?.makeKeyAndVisible()\n }\n}\n\n步骤2:通过场景获取窗口:
\n\nif let windowScene = view.window?.windowScene,\n let window = windowScene.windows.first {\n // 使用 window\n}\n\n或者,如果你需要在应用程序的任何地方获取主窗口,可以保存对SceneDelegate及其window的引用。
优点:
\n- \n
- 完全符合苹果的建议 \n
- 正确处理多个窗口 \n
- 在iPad上安全处理多任务 \n
缺点:
\n- \n
- 需要配置SceneDelegate(如果你尚未迁移) \n
- 代码稍多 \n
3. 通过UIWindowScene.activeWindow(iOS 15+)
\n\n从iOS 15开始,UIWindowScene
博客
书籍推荐
推荐阅读