How to detect if OS X is in dark mode?

MacosCocoaThemes

Macos Problem Overview


My cocoa app has to change its behaviour when run in the new OS X "dark mode".

Is there a way to detect if OS X style is set to this mode?

Macos Solutions


Solution 1 - Macos

Don't think there's a cocoa way of detecting it yet, however you can use defaults read to check whether or not OSX is in dark mode.

defaults read -g AppleInterfaceStyle

Either returns Dark (dark mode) or returns domain pair does not exist.

EDIT:

As Ken Thomases said you can access .GlobalPreferences via NSUserDefaults, so

NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];

If osxMode is nil then it isn't in dark mode, but if osxMode is @"Dark" then it is in dark mode.

Solution 2 - Macos

Swift 2 -> String ("Dark", "Light")

let appearance = NSUserDefaults.standardUserDefaults().stringForKey("AppleInterfaceStyle") ?? "Light"

Swift 3 -> Enum (Dark, Light)

enum InterfaceStyle : String {
   case Dark, Light
    
   init() {
      let type = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") ?? "Light"
      self = InterfaceStyle(rawValue: type)!
    }
}
        
let currentStyle = InterfaceStyle()

Solution 3 - Macos

You can detect this using NSAppearanceCustomization method effectiveAppearance, by checking for darkAqua.

Swift 4 example:

extension NSView {
    var isDarkMode: Bool {
        if #available(OSX 10.14, *) {
            if effectiveAppearance.name == .darkAqua {
                return true
            }
        }
        return false
    }
}

Solution 4 - Macos

You can also wrap it in a boolean if you don't feel like dealing with enums and switch statements:

/// True if the application is in dark mode, and false otherwise
var inDarkMode: Bool {
    let mode = UserDefaults.standard.string(forKey: "AppleInterfaceStyle")
    return mode == "Dark"
}

Works on Swift 4.2

Solution 5 - Macos

I would check against all dark appearances like so

extension NSView {

    var hasDarkAppearance: Bool {
        if #available(OSX 10.14, *) {
            switch effectiveAppearance.name {
            case .darkAqua, .vibrantDark, .accessibilityHighContrastDarkAqua, .accessibilityHighContrastVibrantDark:
                return true
            default:
                return false
            }
        } else {
            switch effectiveAppearance.name {
            case .vibrantDark:
                return true
            default:
                return false
            }
        }
    }
}

Solution 6 - Macos

For working with the new macOS Catalina you need to combine AppleInterfaceStyle with this new value introduced AppleInterfaceStyleSwitchesAutomatically.

Here is some pseudo-code explaining how to:

theme = light //default is light
if macOS_10.15
	if UserDefaults(AppleInterfaceStyleSwitchesAutomatically) == TRUE
		if UserDefaults(AppleInterfaceStyle) == NIL
			theme = dark // is nil, means it's dark and will switch in future to light
		else
			theme = light //means it's light and will switch in future to dark
		endif
	else
		if UserDefaults(AppleInterfaceStyle) == NIL
			theme = light
		else
			theme = dark
		endif
	endif
else if macOS_10.14
	if UserDefaults(AppleInterfaceStyle) == NIL
		theme = light
	else
	    theme = dark
	endif
endif

You can check a macOS sample app here: https://github.com/ruiaureliano/macOS-Appearance.

(Disclaimer: I am the author of this sample app.)

Solution 7 - Macos

This works:

if #available(OSX 10.14, *) {
    inputTextView.textColor = (NSApp.effectiveAppearance.name == NSAppearance.Name.darkAqua ? NSColor.white : NSColor.black)
}

Solution 8 - Macos

2020 | SWIFT 5.1:

WAY 1:

@Environment(\.colorScheme) var scheme

WAY 2:

doesn't update swiftUI in case of theme change. Need to realize additional logic for view upd:

#available(OSX 10.14, *)
static private var isLight: Bool { NSApp.effectiveAppearance.name == NSAppearance.Name.aqua }

#available(OSX 10.14, *)
static private var isDark: Bool { NSApp.effectiveAppearance.name == NSAppearance.Name.darkAqua }

Solution 9 - Macos

This isn't a complete answer to the question because the questioner doesn't say what their use case is. If they want completely different behaviour of their app, the below behaviour doesn't work. However, if they only want to change the colour of some custom view, this is the Apple blessed way.

The thing to do is to stop using absolute colours and start using semantic colours. This means defining a "colour set" for each colour you want to use in the assets catalog. Having defined your colour set, in the inspector, set the device to "Mac" and the appearance to "Any, Light, Dark". You will then get three colour wells, "any" is for legacy operating systems that do not support dark mode, "light" and "dark" should be obvious.

Here is an example:

Defining a colour set that supports dark mode

This defines a colour that will be white in dark mode and black in light mode or on legacy operating systems.

Once you have defined a colour set, you can retrieve the colour in your draw(_ dirtyRect:) as follows:

let strokeColour = NSColor(named: NSColor.Name("gridColour")) ?? NSColor.black

In the above, I default to black if the colour set does not exist to deal with the optional type of NSColor(named:).

Solution 10 - Macos

Take a look at NSAppearance.Name (in Swift speak) - there are variants:

.darkAqua

.accessibilityHighContrastDarkAqua

.accessibilityHighContrastVibrantDark

Solution 11 - Macos

The only safe way to check for dark mode is to use the following:

let viewUsesDarkMode: Bool
if #available(OSX 10.14, *) {
    viewUsesDarkMode = view.effectiveAppearance.bestMatch(from: [.aqua, .darkAqua]) == .darkAqua
} else {
    viewUsesDarkMode = false
}

This is the only solution that works in all cases. Whether you have views with mixed appearances, or if you allow your app to use a different appearance than the system default, or if you configure your system to use the high contrast appearances.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAugustus1View Question on Stackoverflow
Solution 1 - MacosTheAmateurProgrammerView Answer on Stackoverflow
Solution 2 - MacosAndreyView Answer on Stackoverflow
Solution 3 - MacosJames EunsonView Answer on Stackoverflow
Solution 4 - MacosJ.beenieView Answer on Stackoverflow
Solution 5 - MacosEraView Answer on Stackoverflow
Solution 6 - MacosruiaurelianoView Answer on Stackoverflow
Solution 7 - MacosleonardosccdView Answer on Stackoverflow
Solution 8 - MacosAndrewView Answer on Stackoverflow
Solution 9 - MacosJeremyPView Answer on Stackoverflow
Solution 10 - MacosBjoern KriewsView Answer on Stackoverflow
Solution 11 - MacosJakob EggerView Answer on Stackoverflow