我使用核心数据与云工具包,因此要检查iCloud用户状态在应用程序启动。如果出现问题,我想向用户发出一个对话框,我使用UIApplication.shared.keyWindow?. rootviewcontroller ?.present(…)到目前为止。
在Xcode 11 beta 4中,现在有一个新的弃用消息,告诉我:
'keyWindow'在iOS 13.0中已弃用:不应该用于支持多个场景的应用程序,因为它在所有连接的场景中返回一个键窗口
我应该如何呈现对话呢?
我使用核心数据与云工具包,因此要检查iCloud用户状态在应用程序启动。如果出现问题,我想向用户发出一个对话框,我使用UIApplication.shared.keyWindow?. rootviewcontroller ?.present(…)到目前为止。
在Xcode 11 beta 4中,现在有一个新的弃用消息,告诉我:
'keyWindow'在iOS 13.0中已弃用:不应该用于支持多个场景的应用程序,因为它在所有连接的场景中返回一个键窗口
我应该如何呈现对话呢?
当前回答
如果你使用SwiftLint的'first_where'规则,并想静音交战:
UIApplication.shared.windows.first(where: { $0.isKeyWindow })
其他回答
通常使用
斯威夫特5
UIApplication.shared.windows.filter {$0.isKeyWindow}.first
另外,在UIViewController中:
self.view.window
视图。窗口是当前场景的窗口
WWDC 2019:
关键的窗口 手动跟踪窗口
在iPad上介绍多个Windows - WWDC 2019 -视频-苹果开发人员 支持iPad上的多个Windows |苹果开发者文档
(在运行于Xcode 13.2.1的iOS 15.2上测试)
extension UIApplication {
var keyWindow: UIWindow? {
// Get connected scenes
return UIApplication.shared.connectedScenes
// Keep only active scenes, onscreen and visible to the user
.filter { $0.activationState == .foregroundActive }
// Keep only the first `UIWindowScene`
.first(where: { $0 is UIWindowScene })
// Get its associated windows
.flatMap({ $0 as? UIWindowScene })?.windows
// Finally, keep only the key window
.first(where: \.isKeyWindow)
}
}
如果你想在关键的UIWindow中找到呈现的UIViewController,这是另一个你可以发现有用的扩展:
extension UIApplication {
var keyWindowPresentedController: UIViewController? {
var viewController = self.keyWindow?.rootViewController
// If root `UIViewController` is a `UITabBarController`
if let presentedController = viewController as? UITabBarController {
// Move to selected `UIViewController`
viewController = presentedController.selectedViewController
}
// Go deeper to find the last presented `UIViewController`
while let presentedController = viewController?.presentedViewController {
// If root `UIViewController` is a `UITabBarController`
if let presentedController = presentedController as? UITabBarController {
// Move to selected `UIViewController`
viewController = presentedController.selectedViewController
} else {
// Otherwise, go deeper
viewController = presentedController
}
}
return viewController
}
}
你可以把它放在任何你想要的地方,但我个人把它作为UIViewController的扩展。
这让我可以添加更多有用的扩展,比如更容易地呈现UIViewControllers:
extension UIViewController {
func presentInKeyWindow(animated: Bool = true, completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
UIApplication.shared.keyWindow?.rootViewController?
.present(self, animated: animated, completion: completion)
}
}
func presentInKeyWindowPresentedController(animated: Bool = true, completion: (() -> Void)? = nil) {
DispatchQueue.main.async {
UIApplication.shared.keyWindowPresentedController?
.present(self, animated: animated, completion: completion)
}
}
}
我在这里提出的建议在iOS 15中已弃用。现在怎么办?好吧,如果一个应用没有自己的多个窗口,我假设接受的现代方式将是获得应用的第一个connectedscene,强制到一个UIWindowScene,并获得它的第一个窗口。但这几乎正是公认的答案所做的!所以我的变通办法在这一点上感觉相当无力。但是,由于历史原因,我还是让它保持原样。
公认的答案虽然巧妙,但可能过于详尽。你可以更简单地得到完全相同的结果:
UIApplication.shared.windows.filter {$0.isKeyWindow}.first
我还要提醒大家,不要过于认真地对待keyWindow的弃用。完整的警告信息如下:
'keyWindow'在iOS 13.0中已弃用:不应该用于支持多个场景的应用程序,因为它在所有连接的场景中返回一个键窗口
所以如果你不支持iPad上的多窗口,继续使用keyWindow是没有异议的。
如果你的应用还没有更新到采用基于场景的应用生命周期,另一种获得活动窗口对象的简单方法是通过UIApplicationDelegate:
let window = UIApplication.shared.delegate?.window
let rootViewController = window??.rootViewController
NSSet *connectedScenes = [UIApplication sharedApplication].connectedScenes;
for (UIScene *scene in connectedScenes) {
if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]]) {
UIWindowScene *windowScene = (UIWindowScene *)scene;
for (UIWindow *window in windowScene.windows) {
UIViewController *viewController = window.rootViewController;
// Get the instance of your view controller
if ([viewController isKindOfClass:[YOUR_VIEW_CONTROLLER class]]) {
// Your code here...
break;
}
}
}
}