Swift 如何解决:';钥匙窗';在iOS 13.0中被弃用

Swift 如何解决:';钥匙窗';在iOS 13.0中被弃用,swift,ios13,uiwindow,uiscene,Swift,Ios13,Uiwindow,Uiscene,我正在使用云工具包的核心数据,因此必须在应用程序启动期间检查iCloud用户状态。如果出现问题,我想向用户发出一个对话框,我使用UIApplication.shared.keyWindow?.rootViewController?.present(…)到目前为止 在Xcode 11 beta 4中,现在有一条新的弃用消息,告诉我: iOS 13.0中不推荐使用“keyWindow”:不应用于支持多个场景的应用程序,因为它会在所有连接的场景中返回一个关键窗口 如何显示该对话框?这是我的解决方案:

我正在使用云工具包的核心数据,因此必须在应用程序启动期间检查iCloud用户状态。如果出现问题,我想向用户发出一个对话框,我使用
UIApplication.shared.keyWindow?.rootViewController?.present(…)
到目前为止

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

iOS 13.0中不推荐使用“keyWindow”:不应用于支持多个场景的应用程序,因为它会在所有连接的场景中返回一个关键窗口

如何显示该对话框?

这是我的解决方案:

let keyWindow = UIApplication.shared.connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
用法,例如:

keyWindow?.endEditing(true)

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

UIApplication.shared.windows.filter {$0.isKeyWindow}.first
我还要提醒大家,不要过于严肃地对待
keyWindow
的弃用。完整的警告信息如下:

iOS 13.0中不推荐使用“keyWindow”:不应用于支持多个场景的应用程序,因为它会在所有连接的场景中返回一个关键窗口


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

对matt的优秀答案稍加改进,这将更加简单、简短、优雅:

UIApplication.shared.windows.first { $0.isKeyWindow }

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

let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first

对于一个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;
}
我遇到了同样的问题。 我为一个视图分配了一个
newWindow
,并将其设置为
[newwindowmakeyAndVisible]
使用完毕后,将其设置为
[newWindow resignKeyWindow]
然后尝试通过
[UIApplication sharedApplication].keyWindow直接显示原始密钥窗口

在iOS 12上一切正常,但在iOS 13上无法正常显示原始密钥窗口。它显示了一个完整的白色屏幕

我通过以下方式解决了这个问题:

UIWindow *mainWindow = nil;
if ( @available(iOS 13.0, *) ) {
   mainWindow = [UIApplication sharedApplication].windows.firstObject;
   [mainWindow makeKeyWindow];
} else {
    mainWindow = [UIApplication sharedApplication].keyWindow;
}

希望有帮助。

A
ui应用程序
扩展:

extension UIApplication {

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

}
用法:

let myKeyWindow: UIWindow? = UIApplication.shared.keyWindowInConnectedScenes
灵感来源于


以下是一种向后兼容的检测
keyWindow
的方法:

扩展窗口{ 静态变量键:UIWindow{ 如果可用(iOS 13,*){ 返回UIApplication.shared.windows.first{$0.isKeyWindow} }否则{ 返回UIApplication.shared.keyWindow } } }
用法:

let myKeyWindow: UIWindow? = UIApplication.shared.keyWindowInConnectedScenes
如果let keyWindow=UIWindow.key{
//做点什么
}
试试看:

UIApplication.shared.windows.filter { $0.isKeyWindow }.first?.rootViewController!.present(alert, animated: true, completion: nil)
常用

斯威夫特5

UIApplication.shared.windows.filter {$0.isKeyWindow}.first
此外,在UIViewController中:

self.view.window
view.window
是场景的当前窗口

WWDC 2019:

关键窗口

  • 手动跟踪窗口

也适用于Objective-C解决方案

@implementation UIWindow (iOS13)

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

@end

正如许多开发人员要求用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;
}
我在
AppDelegate
类中创建并添加了这个方法作为一个类方法,并以下面非常简单的方式使用它

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

+(UIWindow*)keyWindow;

如果您想在任何ViewController中使用它,只需使用

self.view.window

Berni的代码很好,但当应用程序从后台返回时,它就不起作用了

这是我的代码:

class var safeArea : UIEdgeInsets
{
    if #available(iOS 13, *) {
        var keyWindow = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows
                .filter({$0.isKeyWindow}).first
        // <FIX> the above code doesn't work if the app comes back from background!
        if (keyWindow == nil) {
            keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
        }
        return keyWindow?.safeAreaInsets ?? UIEdgeInsets()
    }
    else {
        guard let keyWindow = UIApplication.shared.keyWindow else { return UIEdgeInsets() }
        return keyWindow.safeAreaInsets
    }
}
类别变量安全区域:UIEdgeInsets
{
如果可用(iOS 13,*){
var keyWindow=UIApplication.shared.connectedScenes
.filter({$0.activationState==.foregroundActive})
.map({$0 as?UIWindowScene})
.compactMap({$0})
.第一个?.窗户
.filter({$0.isKeyWindow})
//如果应用程序从后台返回,上述代码将不起作用!
如果(keyWindow==nil){
keyWindow=UIApplication.shared.windows.first{$0.isKeyWindow}
}
返回键窗口?.safeAreaInsets??UIEdgeInsets()
}
否则{
guard let keyWindow=UIApplication.shared.keyWindow else{return UIEdgeInsets()}
返回键窗口。安全区域插图
}
}

您是在
SceneDelegate
还是
AppDelegate
中执行此操作?还有,你能不能多发一点代码,这样我们就可以复制了?iOS中不再有“keyWindow”的概念,因为一个应用程序可以有多个窗口。您可以将创建的窗口存储在
SceneDelegate
(如果您使用的是
SceneDelegate
)@Sudara:那么,如果我还没有视图控制器,但想发出警报-如何处理场景?如何获取场景,以便可以检索其rootViewController?(所以,简而言之:什么场景相当于UIApplication的“共享”场景?)。。。8-)您只需使用get
isKeyWindow
。在此处测试
activationState
foregroundInactive
也可能是合适的,在我的测试中,如果出现警报,就会出现这种情况。@Drew应该对其进行测试,因为在应用程序启动时,视图控制器已可见,但状态为
foregroundInactive
此代码为我生成keyWindow=nil<代码>马特
解决方案才是有效的。谢谢!在目标c中有办法做到这一点吗?@Allenktv不幸的是
NSArray
没有与
first(其中:)
等效的方法。您可以尝试用
filteredarrayingpredicate:
firstObject:
@allinktv组成一个单行程序。代码在注释部分被弄乱了,所以我在下面发布了一个Objective-C等价物。Xcode 11.2编译器报告了一个错误,并建议首先在
中添加括号及其内容(其中:)
UIApplication.shared.windows.first(其中:{$0.i
self.view.window
class var safeArea : UIEdgeInsets
{
    if #available(iOS 13, *) {
        var keyWindow = UIApplication.shared.connectedScenes
                .filter({$0.activationState == .foregroundActive})
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows
                .filter({$0.isKeyWindow}).first
        // <FIX> the above code doesn't work if the app comes back from background!
        if (keyWindow == nil) {
            keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
        }
        return keyWindow?.safeAreaInsets ?? UIEdgeInsets()
    }
    else {
        guard let keyWindow = UIApplication.shared.keyWindow else { return UIEdgeInsets() }
        return keyWindow.safeAreaInsets
    }
}