Reputation: 715
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?
Upvotes: 68
Views: 29534
Reputation: 41
This can extract the current mode from the GlobalPreferences file.
sw_vers
ProductName: macOS
ProductVersion: 13.5.1
BuildVersion: 22G90
if [[ $(defaults read ~/Library/Preferences/.GlobalPreferences.plist AppleInterfaceStyle 2>/dev/null) = Dark ]]; then
echo 'Dark'
else
echo 'Light'
fi
Upvotes: 4
Reputation:
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
}
}
}
}
Upvotes: 12
Reputation: 11362
2020 | SWIFT 5.1:
@Environment(\.colorScheme) var scheme
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 }
Upvotes: 3
Reputation: 12041
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.
Upvotes: 2
Reputation: 1562
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.)
Upvotes: 13
Reputation: 86691
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:
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:)
.
Upvotes: 3
Reputation: 51
Take a look at NSAppearance.Name (in Swift speak) - there are variants:
.darkAqua
.accessibilityHighContrastDarkAqua
.accessibilityHighContrastVibrantDark
Upvotes: 1
Reputation: 2139
This works:
if #available(OSX 10.14, *) {
inputTextView.textColor = (NSApp.effectiveAppearance.name == NSAppearance.Name.darkAqua ? NSColor.white : NSColor.black)
}
Upvotes: 4
Reputation: 215
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
}
}
Upvotes: 21
Reputation: 831
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
Upvotes: 15
Reputation: 658
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()
Upvotes: 40
Reputation: 9392
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.
Upvotes: 100