12

I want to change my status bar app icon when the user switches from default to dark mode and vice versa (using Swift 3). Here’s what i have so far:

func applicationDidFinishLaunching(_ aNotification: Notification) {
    DistributedNotificationCenter.default().addObserver(self, selector: #selector(darkModeChanged(sender:)), name: "AppleInterfaceThemeChangedNotification", object: nil)
}

...

func darkModeChanged(sender: NSNotification) {
    print("mode changed")
}

Unfortunately, it’s not working. What am I doing wrong?

ixany
  • 5,433
  • 9
  • 41
  • 65

4 Answers4

18

I'm using this Swift 3 syntax successfully:

DistributedNotificationCenter.default.addObserver(self, selector: #selector(interfaceModeChanged(sender:)), name: NSNotification.Name(rawValue: "AppleInterfaceThemeChangedNotification"), object: nil)

func interfaceModeChanged(sender: NSNotification) {
  ...
}
Jeffrey Morgan
  • 556
  • 1
  • 6
  • 12
2

Swift 5, Xcode 10.2.1, macOS 10.14.4

Great stuff. My two cents around @Jeffrey's answer:

extension Notification.Name {
    static let AppleInterfaceThemeChangedNotification = Notification.Name("AppleInterfaceThemeChangedNotification")
}

So one could (instead of rawValue):

func listenToInterfaceChangesNotification() {
    DistributedNotificationCenter.default.addObserver(
        self,
        selector: #selector(interfaceModeChanged),
        name: .AppleInterfaceThemeChangedNotification,
        object: nil
    )
}

Remember the @objc attribute:

@objc func interfaceModeChanged() {
    // Do stuff.
}
backslash-f
  • 7,923
  • 7
  • 52
  • 80
1

So, my little additions as well:

enum InterfaceStyle: String {
    case Light
    case Dark
    case Unspecified
}

extension Notification.Name {
    static let AppleInterfaceThemeChangedNotification = Notification.Name("AppleInterfaceThemeChangedNotification")
}

extension NSViewController {
    var interfaceStyle: InterfaceStyle {
        let type = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") ?? "Unspecified"
        return InterfaceStyle(rawValue: type) ?? InterfaceStyle.Unspecified
    }
}

and somewhere in a NSViewController:

        DistributedNotificationCenter.default.addObserver(forName: .AppleInterfaceThemeChangedNotification,
                                                          object: nil, queue: OperationQueue.main) {
            [weak weakSelf = self] (notification) in                // add an observer for a change in interface style
            weakSelf?.setAppearance(toStyle: weakSelf!.interfaceStyle)
        }

where setAppearance reacts on the change of style.

FPP
  • 278
  • 3
  • 7
0

If you simply need to update icon images for dark mode, you can do this without notifications by creating a dynamic image that updates automatically.

From Apple's documentation:

To create an image that draws its content dynamically, use the init(size:flipped:drawingHandler:) method to initialize your image with a custom drawing handler block. AppKit calls your handler block whenever the system appearance changes, giving you a chance to redraw the image using the new appearance.

Robin Stewart
  • 3,147
  • 20
  • 29