如何解决:iOS 13.0中不推荐使用“ keyWindow”

Har*_*rdy 43 uikit uiwindow uiapplication uiscene

我正在将Core Data和Cloud Kit一起使用,因此必须在应用程序启动期间检查iCloud用户状态。如果出现问题,我想向用户发出一个对话框UIApplication.shared.keyWindow?.rootViewController?.present(...),直到现在我还是这样做。

在Xcode 11 beta 4中,现在有一条新的弃用消息,告诉我:

iOS 13.0中已弃用“ keyWindow”:不应将其用于支持多个场景的应用程序,因为它会返回所有已连接场景的关键窗口

我该如何显示对话框?

Vad*_*vin 47

这是一种向后兼容的检测方式keyWindow

extension UIWindow {
    static var key: UIWindow? {
        if #available(iOS 13, *) {
            return UIApplication.shared.windows.first { $0.isKeyWindow }
        } else {
            return UIApplication.shared.keyWindow
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

if let keyWindow = UIWindow.key {
    // Do something
}
Run Code Online (Sandbox Code Playgroud)

  • 这是最优雅的答案,并展示了 Swift“扩展”是多么美丽。 (5认同)
  • 可用性检查几乎没有必要,因为 `windows` 和 `isKeyWindow` 自 iOS 2.0 以来就已经存在,而 `first(where:)` 自 Xcode 9.0 / Swift 4 / 2017 以来就已经存在。 (2认同)

mat*_*att 41

公认的答案虽然巧妙,但可能过于详尽。您可以更简单地获得完全相同的结果:

UIApplication.shared.windows.filter {$0.isKeyWindow}.first
Run Code Online (Sandbox Code Playgroud)

我还要提醒您,keyWindow不要过分重视弃用。完整的警告消息为:

iOS 13.0中已弃用“ keyWindow”:不应将其用于支持多个场景的应用程序,因为它会返回所有已连接场景的关键窗口

因此,如果您不支持iPad上的多个窗口,则不反对继续使用keyWindow

  • 现在也可以简化为“UIApplication.shared.windows.first(where: \.isKeyWindow)” (24认同)
  • @Mario这不是窗口数组中的第一个窗口。它是 windows 数组中的第一个 _key_ 窗口。 (2认同)
  • @Mario但是这个问题的前提是只有一个场景。正在解决的问题仅仅是弃用某个属性。显然,如果您的 iPad 上实际上有多个窗口,那么情况会变得更加复杂!如果您真的想编写一个多窗口 iPad 应用程序,祝您好运。 (2认同)
  • @ramzesenok 当然可以更好。但这并没有错。相反,我是第一个建议向应用程序请求一个作为关键窗口的窗口可能就足够了,从而避免弃用“keyWindow”属性。因此获得了赞成票。如果您不喜欢它,请投反对票。但不要告诉我更改它以匹配其他人的答案;正如我所说,那是错误的。 (2认同)
  • @dadalar 是的,我真的很喜欢这种语法(Swift 5.2 中的新语法)。 (2认同)

iHT*_*boy 37

通常使用

斯威夫特 5

UIApplication.shared.windows.filter {$0.isKeyWindow}.first
Run Code Online (Sandbox Code Playgroud)

另外?在 UIViewController 中:

self.view.window
Run Code Online (Sandbox Code Playgroud)

view.window 是场景的当前窗口

2019 年世界开发者大会: 在此处输入图片说明

关键窗口

  • 手动跟踪窗口


ber*_*rni 33

这是我的解决方案:

let keyWindow = UIApplication.shared.connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
Run Code Online (Sandbox Code Playgroud)

用法,例如:

keyWindow?.endEditing(true)
Run Code Online (Sandbox Code Playgroud)

  • 谢谢 - 不是很直观就能发现的东西...... 8-) (11认同)
  • 在这里测试“activationState”值“foregroundInactive”也可能是合适的,在我的测试中,如果出现警报,就会出现这种情况。 (4认同)
  • 这段代码为我生成 keyWindow = nil 。“matt”解决方案是有效的。 (3认同)
  • 在 applicationWillEnterForeground 期间调用此解决方案实际上对我不起作用。@matt 提出的解决方案有效。 (3认同)
  • @Drew 应该对其进行测试,因为在应用程序启动时视图控制器已经可见,但状态为“foregroundInactive” (2认同)

use*_*649 23

对于 Objective-C 解决方案

+(UIWindow*)keyWindow
{
    UIWindow        *foundWindow = nil;
    NSArray         *windows = [[UIApplication sharedApplication]windows];
    for (UIWindow   *window in windows) {
        if (window.isKeyWindow) {
            foundWindow = window;
            break;
        }
    }
    return foundWindow;
}
Run Code Online (Sandbox Code Playgroud)

  • 不要忘记在标头声明中添加“nullable”! (2认同)

pom*_*mmy 15

在matt的出色回答上略有改进,这甚至更简单,更短且更优雅:

UIApplication.shared.windows.first { $0.isKeyWindow }
Run Code Online (Sandbox Code Playgroud)

  • 现在也可以简化为“UIApplication.shared.windows.first(where: \.isKeyWindow)” (6认同)
  • 不幸的是“windows”现在也已被弃用。 (6认同)
  • 谢谢你!在目标c中有没有办法做到这一点? (2认同)
  • FWIW,在 iOS 15+ 的多窗口应用程序中,多个场景可以有一个设置了“isKeyWindow”的窗口。抓住“第一个”会导致不一致的行为。 (2认同)

Dan*_*orm 12

一个UIApplication延伸:

extension UIApplication {

    /// The app's key window taking into consideration apps that support multiple scenes.
    var keyWindowInConnectedScenes: UIWindow? {
        return windows.first(where: { $0.isKeyWindow })
    }

}
Run Code Online (Sandbox Code Playgroud)

用法:

let myKeyWindow: UIWindow? = UIApplication.shared.keyWindowInConnectedScenes
Run Code Online (Sandbox Code Playgroud)


小智 10

支持 iOS 13 及更高版本。

为了继续使用与旧版 iOS 版本类似的语法,UIApplication.shared.keyWindow请创建此扩展:

extension UIApplication {
    var mainKeyWindow: UIWindow? {
        get {
            if #available(iOS 13, *) {
                return connectedScenes
                    .flatMap { ($0 as? UIWindowScene)?.windows ?? [] }
                    .first { $0.isKeyWindow }
            } else {
                return keyWindow
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

if let keyWindow = UIApplication.shared.mainKeyWindow {
    // Do Stuff
}
Run Code Online (Sandbox Code Playgroud)


Cha*_*mji 9

理想情况下,由于它已被弃用,我建议您将窗口存储在 SceneDelegate 中。但是,如果您确实需要临时解决方法,则可以创建一个过滤器并像这样检索 keyWindow。

let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
Run Code Online (Sandbox Code Playgroud)


Rém*_* B. 9

(在 Xcode 13.2.1 上运行的 iOS 15.2 上进行测试)

extension UIApplication {
    
    var keyWindow: UIWindow? {
        // Get connected scenes
        return self.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)
    }
    
}
Run Code Online (Sandbox Code Playgroud)

如果您想找到UIViewControllerkey 中提供的内容UIWindow,这里是另一个extension您可能会发现有用的:

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
    }
    
}
Run Code Online (Sandbox Code Playgroud)

您可以将其放在任何您想要的位置,但我个人将其添加为extensionto UIViewController

这使我可以添加更多有用的扩展,例如UIViewController更容易呈现的扩展:

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)
        }
    }
    
}
Run Code Online (Sandbox Code Playgroud)


Par*_*shi 7

由于许多开发人员要求使用Objective C代码来替换此弃用的代码。您可以使用下面的代码来使用 keyWindow。

+(UIWindow*)keyWindow {
    UIWindow        *windowRoot = nil;
    NSArray         *windows = [[UIApplication sharedApplication]windows];
    for (UIWindow   *window in windows) {
        if (window.isKeyWindow) {
            windowRoot = window;
            break;
        }
    }
    return windowRoot;
}
Run Code Online (Sandbox Code Playgroud)

我在类中创建并添加了此方法AppDelegate作为类方法,并通过下面非常简单的方法使用它。

[AppDelegate keyWindow];
Run Code Online (Sandbox Code Playgroud)

不要忘记在 AppDelegate.h 类中添加此方法,如下所示。

+(UIWindow*)keyWindow;
Run Code Online (Sandbox Code Playgroud)


小智 7

如果你想在任何 ViewController 中使用它,那么你可以简单地使用。

self.view.window
Run Code Online (Sandbox Code Playgroud)


小智 5

尝试这样做:

UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController!.present(alert, animated: true, completion: nil)
Run Code Online (Sandbox Code Playgroud)


Mad*_*Wai 5

对于 Objective-C 解决方案也是如此

@implementation UIWindow (iOS13)

+ (UIWindow*) keyWindow {
   NSPredicate *isKeyWindow = [NSPredicate predicateWithFormat:@"isKeyWindow == YES"];
   return [[[UIApplication sharedApplication] windows] filteredArrayUsingPredicate:isKeyWindow].firstObject;
}

@end
Run Code Online (Sandbox Code Playgroud)