3

I would like to use the delegate methods of the UISceneSession lifecycle to help inform my Mac Catalyst app when the user moves their focus away from the app (window) and then comes back to the app (window).

When the app first launches on Mac sceneWillEnterForeground and sceneDidBecomeActive are successfully called, however when I focus on another app then delegate methods such as sceneWillEnterForeground are not called. Why?

Cesare
  • 9,139
  • 16
  • 78
  • 130

2 Answers2

11

If you check NSWindowDelegate.windowDidBecomeMain(_:) you'll notice that its parameter is a notification whose name is NSWindowDidBecomeMainNotification.

So, you can observe that notification to get notified whenever a window in your app becomes focused:

NotificationCenter.default.addObserver(forName: .init("NSWindowDidBecomeMainNotification"), object: nil, queue: nil) { notification in
    print("This window became focused:", notification.object)
}

And you can also observe NSWindowDidResignMainNotification to get notified when a window in your app lost focus.

Hejazi
  • 16,587
  • 9
  • 52
  • 67
  • The answer works, thanks for that. One thing, `.init(rawValue: "NSWindowDidBecomeMainNotification")` works instead of `.init("NSWindowDidBecomeMainNotification")`. – badhanganesh Mar 18 '21 at 15:43
  • 1
    This works for a single scene Mac Catalyst app! However, in a multiple Scene(several simultaneously open ViewControllers) app, the notification triggers whenever any of the open windows gets focus. I have been digging around hoping to find an event specific to a single scene activating. The scene delegate methods do not trigger after the initial opening and activation event. – BlueskyMed Jun 07 '21 at 16:26
2

Thanks to https://developer.apple.com/forums/thread/681591 , I discovered that traitCollection.activeAppearance is very useful for this purpose.

In SceneDelegate, you can add:

func windowScene(_ windowScene: UIWindowScene, didUpdate previousCoordinateSpace: UICoordinateSpace, interfaceOrientation previousInterfaceOrientation: UIInterfaceOrientation, traitCollection previousTraitCollection: UITraitCollection) {
    if #available(macCatalyst 14.0, *) {
        let newActiveAppearance = windowScene.traitCollection.activeAppearance
        if newActiveAppearance != previousTraitCollection.activeAppearance && newActiveAppearance == .active {
            //do stuff
        }
    }
}

Or in your ViewController, you can add:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    if #available(macCatalyst 14.0, *) {
        let newActiveAppearance = traitCollection.activeAppearance
        if newActiveAppearance != previousTraitCollection.activeAppearance && newActiveAppearance == .active {
            //do stuff
        }
    }
}
salami
  • 141
  • 7