Fabiosoft
Fabiosoft

Reputation: 1201

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

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.

Upvotes: 10

Views: 10490

Answers (7)

Nasir
Nasir

Reputation: 1763

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

Upvotes: 0

Alessandro Ornano
Alessandro Ornano

Reputation: 35402

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
}

Upvotes: 13

Georges Samaha
Georges Samaha

Reputation: 11

You could try:

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

Hope this helps!

Upvotes: 1

NSProgrammer
NSProgrammer

Reputation: 2396

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.

Upvotes: 7

Iliyan Kafedzhiev
Iliyan Kafedzhiev

Reputation: 397

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

Upvotes: 0

Mark
Mark

Reputation: 7419

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

Upvotes: 2

Gabe
Gabe

Reputation: 2267

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.

Upvotes: -2

Related Questions