解決方法: 'keyWindow'はiOS 13.0で廃止されました


113

Cloud KitでCore Dataを使用しているため、アプリケーションの起動時にiCloudユーザーのステータスを確認する必要があります。問題が発生した場合は、ユーザーにダイアログを発行したいので、これまで使用UIApplication.shared.keyWindow?.rootViewController?.present(...)してきました。

Xcode 11ベータ4では、新しい非推奨メッセージが次のように表示されます。

'keyWindow'はiOS 13.0で廃止されました:複数のシーンをサポートするアプリケーションには使用しないでください。接続されているすべてのシーンでキーウィンドウが返されます。

代わりにダイアログをどのように提示しますか?


あなたはこれをしていますSceneDelegateAppDelegate?そして、複製できるようにもう少しコードを投稿してもらえますか?
dfd

1
1つのアプリが複数のウィンドウを持つことができるため、iOSには「keyWindow」の概念はもうありません。あなたは、あなたの中に作成ウィンドウ格納することができSceneDelegate(あなたが使用している場合SceneDelegate
Sudara

1
@スダラ:それで、もし私がまだビューコントローラーを持っていないが、アラートを提示したいなら-どのようにシーンでそれをするのですか?rootViewControllerを取得できるようにシーンを取得する方法は?(つまり、簡単に言うと、UIApplicationの「共有」に相当するシーンは何ですか?)
Hardy

回答:


94

これは私の解決策です:

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

使用例:

keyWindow?.endEditing(true)

4
ありがとう-直感的に理解できるものではありません... 8
Hardy

その間、私は複数のシーンのサンプル(developer.apple.com/documentation/uikit/app_and_environment/…)でアプローチをテストし、すべて期待どおりに機能しました。
berni

1
ここでactivationState値をテストすることも適切な場合があります。foregroundInactive私のテストでは、警告が表示された場合がそうです。
ドリュー

1
@Drewこれはテストする必要があります。アプリの起動時にビューコントローラーは既に表示されていますが、状態はforegroundInactive
Gargo

2
このコードは、keyWindow = nilを生成します。matt解決策は機能するものです。
アヒル

168

受け入れられた答えは巧妙ですが、過度に複雑な場合があります。まったく同じ結果をより簡単に得ることができます:

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

また、の廃止をkeyWindowあまり真剣に受け止めないでください。完全な警告メッセージは次のとおりです。

'keyWindow'はiOS 13.0で廃止されました:複数のシーンをサポートするアプリケーションには使用しないでください。接続されているすべてのシーンでキーウィンドウが返されます。

したがって、iPadで複数のウィンドウをサポートしていない場合でも、先に進んで使用し続けることに異議はありませんkeyWindow


let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "homeVC") as! UITabBarController UIApplication.shared.keyWindow?.rootViewController = vciOS 13とカードビューの場合、ユーザーがログアウトした後、ビュー階層のメインアプリのログイン画面にプッシュされ、下にスワイプして戻ることができるため、このようなセグエをどのように処理しますか?問題があります。
Lukas Bimba

1
@Marioこれは、windows配列の最初のウィンドウではありません。これは、windows配列の最初のキーウィンドウです。
マット

1
@Marioしかし、質問では、シーンが1つしかないことを前提としています。解決されている問題は、特定のプロパティの廃止にすぎません。iPadに実際に複数のウィンドウがある場合、明らかに人生ははるかに複雑です!あなたが本当にマルチウィンドウのiPadアプリを書こうとしているなら、あなたに幸運を。
マット

1
@ramzesenokもちろん良いかもしれません。しかし、それは間違いではありません。それどころか、私は最初に、アプリケーションにキーウィンドウであるウィンドウを要求するだけで十分である可能性があることを提案し、keyWindowプロパティの非推奨を回避しました。したがって賛成票。気に入らない場合は、反対票を投じてください。しかし、他の誰かの答えに合わせて変更するように言わないでください。私が言ったように、それは間違っているでしょう。
マット

5
これは、次のように簡略化することもできますUIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

62

マットの優れた答えをわずかに改善して、これはさらに単純で短く、よりエレガントです:

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

1
ありがとうございました!これを目的cで行う方法はありますか?
Allenktv

1
@Allenktv残念ながらにNSArray相当するものはありませんfirst(where:)。あなたはとワンライナーを構成しようとするかもしれないfilteredArrayUsingPredicate:firstObject:
ポミー

1
@Allenktvコメントセクションでコードが破損しているため、以下のObjective-Cの同等物を投稿しました。
user2002649

Xcode 11.2コンパイラはこの回答でエラーを報告し、括弧とその内容をfirst(where:)次のように追加するよう提案しました:UIApplication.shared.windows.first(where: { $0.isKeyWindow })
Yassine ElBadaoui

1
これは、次のように簡略化することもできますUIApplication.shared.windows.first(where: \.isKeyWindow)
dadalar

25

以下に、下位互換性のある検出方法を示しkeyWindowます。

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

使用法:

if let keyWindow = UIWindow.key {
    // Do something
}

2
これは最もエレガントな答えであり、Swift extensionがどれほど美しいかを示しています。🙂
クリフトン関節唇

1
可用性チェックがあるため、ほとんど必要であるwindowsisKeyWindowのiOS 2.0以降の周りされており、first(where:)Xcodeの9.0 /スイフト以来4/2017
pommy

UIApplication.keyWindowiOS 13.0では非推奨になりました:@available(iOS、導入:2.0、非推奨:13.0、メッセージ:「接続されているすべてのシーンでキーウィンドウを返すため、複数のシーンをサポートするアプリケーションには使用しないでください」)
Vadim Bulavin


14

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;
}

10

理想的には非推奨になっているので、ウィンドウをSceneDelegateに保存することをお勧めします。ただし、一時的な回避策が必要な場合は、次のようにフィルターを作成してkeyWindowを取得できます。

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

9

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 })
    }

}

使用法:

let myKeyWindow: UIWindow? = UIApplication.shared.keyWindowInConnectedScenes

2

それで試してください:

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

1
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;
            }
        }
    }
}

1

Objective-Cソリューションの場合も

@implementation UIWindow (iOS13)

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

@end

1
- (UIWindow *)mainWindow {
    NSEnumerator *frontToBackWindows = [UIApplication.sharedApplication.windows reverseObjectEnumerator];
    for (UIWindow *window in frontToBackWindows) {
        BOOL windowOnMainScreen = window.screen == UIScreen.mainScreen;
        BOOL windowIsVisible = !window.hidden && window.alpha > 0;
        BOOL windowLevelSupported = (window.windowLevel >= UIWindowLevelNormal);
        BOOL windowKeyWindow = window.isKeyWindow;
        if(windowOnMainScreen && windowIsVisible && windowLevelSupported && windowKeyWindow) {
            return window;
        }
    }
    return nil;
}


0

ベルニの答えに触発された

let keyWindow = Array(UIApplication.shared.connectedScenes)
        .compactMap { $0 as? UIWindowScene }
        .flatMap { $0.windows }
        .first(where: { $0.isKeyWindow })

0

この非推奨の代替品の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;

-1

同じ問題に遭遇しました。私newWindowはビューにを割り当てて設定しまし[newWindow makeKeyAndVisible]; た使用が終了したら、設定して[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;
}

それが役に立てば幸い。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.