Setting Observer for Swift Objects/Properties
Asked Answered
R

1

2

I've been looking for a way to trigger a method when the number of displays connected to a mac changes. I know I can get the value of NSScreen.screens.count, but I need to find a way to create a notification or something when that value changes or something else that would indicate a change in the number of displays connected.

I have tried KVO examples here and here, but in order for either of those to work there needs to be an event that triggers the methods inside the class.

In essence this is what I would like to do based on the first link:


class EventObserverDemo {
    var statusObserver: NSKeyValueObservation?
    var objectToObserve: NSScreen.screens.count?
    
    func registerAddObserver() -> Void {
        statusObserver = objectToObserve?.observe(NSScreen.screens.count, options: [.new, .old], changeHandler: {[weak self] (NSScreen.screens.count, change) in
            if let value = change.newValue {
                // observed changed value and do the task here on change.
                print("The display count has changed.")
            }
        })
    }
    
    func unregisterObserver() -> Void {
        if let sObserver = statusObserver {
            sObserver.invalidate()
            statusObserver = nil
        }
    }
}

I tried using a notification that used NSScreen.colorSpaceDidChangeNotification but that does not trigger a notification if a display is disconnected.

I would like to find a way to detect any time an external display is connected or disconnected. There has to be something I haven't found yet because whenever I plug in an external display to my mac I see the screen on the main display change, so there's some kind of notification that something changed whether I plug in a display or unplug it from my mac.

I looked at the didSet function, but I couldn't figure out a way to implement that on NSScreen.screens.count property.

I also looked into a property wrapper for NSScreen.screens.count but again I couldn't figure that out either.

Riannon answered 24/11, 2021 at 21:41 Comment(3)
have you tried NSApplicationDidChangeScreenParametersNotification? Also i think you should have a look at #29089883Cliquish
I never once found that notification. Had I found it I wouldn't have wasted hours scouring the web. I appreciate it! Oh, and that linked article really is eye opening about the arrays. It now makes sense what I was encountering with the NSScreen.screens.count being a constant, yet it will change whenever another screen is connected/disconnected.Riannon
Just for reference, it is found at this location developer.apple.com/documentation/appkit/nsscreen/…Cliquish
A
2

You can observe the NSApplication.didChangeScreenParametersNotification notification. This example will only print once each time a display is either connected or disconnected, and what the change was in the number of screens.

Code:

class EventObserverDemo {
    var lastCount = NSScreen.screens.count

    init() {
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(trigger),
            name: NSApplication.didChangeScreenParametersNotification,
            object: nil
        )
    }

    @objc private func trigger(notification: NSNotification) {
        let newCount = NSScreen.screens.count

        if newCount != lastCount {
            print("Switched from \(lastCount) to \(newCount) displays")
            lastCount = newCount
        }
    }
}

You don't need to remove/invalidate the observer either, easier to let the system handle it:

If your app targets iOS 9.0 and later or macOS 10.11 and later, you do not need to unregister an observer that you created with this function. If you forget or are unable to remove an observer, the system cleans up the next time it would have posted to it.

Aili answered 24/11, 2021 at 23:0 Comment(3)
I scoured the web for hours and the docs and never came across this notification. So simple... Thank you very much!Riannon
@Riannon Neither did I, but I knew a few of these notifications had been renamed like that so I found it like that :)Aili
Just implemented this in my app and It is interesting that the notification fires whenever the window on that display's fullscreen toggle is changed. No big deal, but I found that surprising. I didn't think toggling full screen on or off would be considered a "configuration change." Anyway, it meets my needs and that's all that matters at this point.Riannon

© 2022 - 2024 — McMap. All rights reserved.