gbdavid
gbdavid

Reputation: 1759

How can I read the user selection for OS X menu bar in Xcode (Swift)

I'm creating an agent app for OS X in swift (only showing the app icon in the menu bar). I'm loading the icon for the app from the AppDelegate using:

 statusItem.image = NSImage(named: "BlackIcon")

and it works fine.

However, if the user has chosen to use the dark menu bar from the System Preferences -> General, the user won't see the icon as it's black.

enter image description here

So I need to display a different 'WhiteIcon' to the user if they have the option selected.

How can I check whether the user has this option active from my app?

Upvotes: 3

Views: 814

Answers (2)

Obj-Swift
Obj-Swift

Reputation: 2942

It appears that you are trying to invert menulet icon color for dark mode. By default OSX handles darkmode and inverts the image color, however you need to specifically add [image setTemplate:YES] to have this work for you if it already doesnt.

Objective-c:

self.statusItem = [[NSStatusBar systemStatusBar]     
statusItemWithLength:NSSquareStatusItemLength];
NSImage *image = [NSImage imageNamed:@"statusItemIcon"];
[image setTemplate:YES];
[self.statusItem setImage:image];

swift: (Originally answered by Zhi-Wei Cai at link below)

var isDark = false

func isDarkMode() {
  // Swift2
  // isDark = NSAppearance.currentAppearance().name.hasPrefix("NSAppearanceNameVibrantDark") 

  // Swift3+
  isDark = NSAppearance.current.name.rawValue.hasPrefix("NSAppearanceNameVibrantDark") 
}

override func drawRect(dirtyRect: NSRect) {
super.drawRect(dirtyRect)
isDarkMode()
// Now use "isDark" to determine the drawing colour.
if isDark {
    // ...
 } else {
    // ...
 }
}

This answer explains it in the detail: NSStatusItem change image for dark tint

Upvotes: 1

Jason M.
Jason M.

Reputation: 653

With Swift 3.0, you can use the UserDefaults to access the macOS appearance, or "AppleInterfaceStyle", using the following code:

let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")

If the user has enabled dark mode, the defaults will return a string "Dark". If they have the "light mode" enabled it will return nil. So you will need to wrap that in the following code:

if UserDefaults.standard.string(forKey: "AppleInterfaceStyle") == "Dark" {
    statusItem.image = NSImage(named: "WhiteIcon")
} else {
    statusItem.image = NSImage(named: "BlackIcon")
}

I'm sure there might be a cleaner way, maybe with a guard but that will get you where you need to be in Swift 3.0

Edit:

The above code will determine the users current "mode". However, using a simple check for the users preference will not result in the correct behavior (e.g. it will only fire when the application starts).

The correct method of performing this is to set the menu icon as a black icon. Then, browse to the asset in the Asset Catalog, and select the menu icon. With the menu selected, browse to the Attributes Inspector and make sure the image is checked with a “Mac” device. Then choose “Render As” set to “Template Image”.

This only requires you to have one icon, in black, and macOS will handle the conversion of the images from dark to light modes.

Image Attributes Inspector

Upvotes: 4

Related Questions