How get current keywindow equivalent for multi window SceneDelegate Xcode 11?
Asked Answered
F

7

10

I'm converting my iOS13 app for iPadOS to SceneDelegate (multi window).

How can I get the current UIWindow from the current SceneDelegate?

I know that a can access the current scene using UIView.window or UIViewController.view.window, but I have a non UI class (AppDelegate) where I need to get the window (keyWindow until iOS12) to show a snack bar on top of everything.

I used to do [UIApplication sharedApplication].keyWindow but now, of course, that's wrong.

Fredra answered 12/7, 2019 at 14:42 Comment(0)
A
13

Swift 5.x / iOS 13.x

Inspired by many solutions, to get back my old window property (searching it inside the connectedScenes) I've realized a little extension:

extension UIApplication {
    var currentWindow: UIWindow? {
        connectedScenes
        .filter({$0.activationState == .foregroundActive})
        .map({$0 as? UIWindowScene})
        .compactMap({$0})
        .first?.windows
        .filter({$0.isKeyWindow}).first
    }
}

Usage:

if let window = UIApplication.shared.currentWindow { 
   // do whatever you want with window
}
Assembled answered 2/11, 2019 at 17:31 Comment(5)
i think on ios13 when windows are side by side... both are "foregroundActive", but i can't confirm.Fredra
yes, I can confirm this, still have no clue how to access the active (or who click the applink)Rodi
@JeffersonSetiawan What about removing .first from the last line, and adding this line to the end the extension : .filter({$0.isFocused == true}).first. Can you try this one?Assembled
does not working using .filter({$0.isFocused == true}).firstFabien
You can collapse .map and .compactMap with .compactMap { $0 as? UIWindowScene }?Earl
M
7

You'll want to simply iterate through all your windows and find the key window manually.

for (UIWindow *window in [UIApplication sharedApplication].windows) {
    if (window.isKeyWindow) {
        // you have the key window
        break;
    }
}

DO NOT use UISceneActivationStateForegroundActive as the check. That means something different and folks are introducing bugs by using logic to find the first UISceneActivationStateForegroundActive window scene.

Marksman answered 17/9, 2019 at 16:23 Comment(0)
T
2

It sounds like you probably want to move this logic to your SceneDelegate.

The SceneDelegate now has knowledge of whether the window is connected to the scene, so it might make sense to have every connected scene listen to whatever event is driving the snack bar, and the show it on its window. This would then result in every visible window showing the snack bar (1 or more).

Turman answered 30/8, 2019 at 4:7 Comment(0)
C
1

You could try:

    if let window = UIApplication.shared.windows.first(where: { (window) -> Bool in window.isKeyWindow}) {
    //your code
    }

Hope this helps!

Canescent answered 9/10, 2019 at 0:18 Comment(0)
M
0

Now you have more than one window, one for each scene. First, you have to answer which one you need at the moment of usage.

Probably you want to get the window of the currently active scene then you can use this:

    UIWindow* window = nil;
    if (@available(iOS 13.0, *))
    {
        for (UIWindowScene* wScene in [UIApplication sharedApplication].connectedScenes)
        {
            if (wScene.activationState == UISceneActivationStateForegroundActive)
            {
                window = wScene.windows.firstObject;

                break;
            }
        }
    }
Manes answered 5/9, 2019 at 13:8 Comment(4)
this is incorrect. That is not what UISceneActivationStateForegroundActive means. You want window.isKeyWindowMarksman
The question is "What is the way to get UIWindow from non-UI class. On the other hand, the key window is: "The key window receives keyboard and other non-touch related events. Only one window at a time may be the key window." You can have more than one active windows that are touch-able at the same time on the screen and only one of them is keyWindow, but it might not be the wanted window. There is no absolutely correct answer to this question, that is why my solution is a proposal in one concrete scenario.Manes
And that is why Apple deprecated, keyWindow property from UIApplication.Manes
I see what you were getting at. I suppose in a world of rapid stack overflow copying and pasting I wanted to interject to avoid folks misunderstanding and applying code that will lead to bugs (as my developer did copying this code to our code base). I apologize for pushing that your intent was wrong, that was unfair.Marksman
G
0

For SwiftUI, Tested in iOS 16+

extension UIApplication {
    var currentWindow: UIWindow? {
        connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .first?
            .windows
            .first
    }
}

It's usage.

guard let anchor = UIApplication.shared.currentWindow else {
   fatalError("Window did not found in Hierarchy")
}
Gleeson answered 25/6, 2022 at 5:4 Comment(0)
R
-2

I haven't tried this yet, but you should be able to get all the windows with [UIApplication sharedApplication].windows and then pick if you want to show the snack bar on all of the windows or one window.

Remittance answered 23/8, 2019 at 18:36 Comment(2)
With this method you get an array of windows...but you don’t know what’s the currentFredra
This has been deprecated since iOS 15.0Entrant

© 2022 - 2024 — McMap. All rights reserved.