如何检测OS X是否在深色模式下?

64

我的Cocoa应用程序在新的OS X“暗黑模式”下运行时必须更改其行为。

有没有一种方法可以检测操作系统的样式是否设置为此模式?


2
瞎猜一下 - 你尝试过监听 NSScreenColorSpaceDidChangeNotification 或检查 NSScreencolorSpace 属性吗?我现在用的是Mav,无法检查。 - Warren Burton
12个回答

92

目前好像没有一种可靠的方法来检测 Cocoa 是否处于暗黑模式下,但是您可以使用 defaults read 命令来检查 macOS 系统是否启用了暗黑模式。

defaults read -g AppleInterfaceStyle

返回Dark(暗色模式)或返回域对不存在。

编辑:

如Ken Thomases所说,您可以通过NSUserDefaults访问.GlobalPreferences,因此

NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
如果 osxMode 的值是 nil,那么它不在暗黑模式中,但如果 osxMode 的值是 @"Dark",则它处于暗黑模式中。

4
defaults 命令只是 CFPreferences API 的一个包装器,NSUserDefaults 也是如此。因此,您可以使用这些 API 中的任何一个,而不是调用 defaults - Ken Thomases
2
@houbysoft 使用KVO - TheAmateurProgrammer
9
在Catalina上无法工作:“(kCFPreferencesAnyApplication,AppleInterfaceStyle)的域/默认对不存在”。 - bas
7
“不存在”可能意味着“亮色模式”。 - JeremyP
6
我可以确认(在Catalina系统上),如果开启了深色模式,在终端输入defaults read -g AppleInterfaceStyle命令会返回“Dark”,否则会返回“The domain/default pair of (kCFPreferencesAnyApplication, AppleInterfaceStyle) does not exist”。这个结果相当不可预测。:) - Victor
显示剩余14条评论

40

Swift 2 -> 字符串 ("暗", "亮")

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

Swift 3 -> 枚举类型(暗色,明亮)

enum InterfaceStyle : String {
   case Dark, Light

   init() {
      let type = UserDefaults.standard.string(forKey: "AppleInterfaceStyle") ?? "Light"
      self = InterfaceStyle(rawValue: type)!
    }
}

let currentStyle = InterfaceStyle()

4
不错,我不知道枚举也可以有一个init()方法! - SilverWolf

21
您可以使用NSAppearanceCustomization方法effectiveAppearance来检测,通过检查darkAqua来确定。
Swift 4示例:
extension NSView {
    var isDarkMode: Bool {
        if #available(OSX 10.14, *) {
            if effectiveAppearance.name == .darkAqua {
                return true
            }
        }
        return false
    }
}

我正在使用[NSAppearance.Name.darkAqua,NSAppearance.Name.vibrantDark] .contains(effectiveAppearance.name)来检查两种暗色外观。 - GP89

15

如果你不想处理枚举和switch语句,你也可以将它包装在一个布尔值中:

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

适用于Swift 4.2


13

要在新的macOS Catalina上工作,您需要将AppleInterfaceStyle与这个新值AppleInterfaceStyleSwitchesAutomatically 结合使用。

以下是一些伪代码,说明如何操作:

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

您可以在此处查看一个 macOS 示例应用程序:https://github.com/ruiaureliano/macOS-Appearance

(免责声明:本示例应用程序的作者为我自己。)


12
我会检查所有深色外观,像这样
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
            }
        }
    }
}

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

3
这并不是完整的答案,因为提问者没有说明他们的用例。如果他们想要完全不同的应用程序行为,则下面的行为不起作用。但是,如果他们只想更改某些自定义视图的颜色,则这是苹果认可的方式
要做的事情是停止使用绝对颜色,开始使用语义颜色。这意味着为要在资源目录中使用的每种颜色定义一个“颜色集”。在定义了您的颜色集之后,在检查器中将设备设置为“Mac”,外观设置为“Any、Light、Dark”。然后,您将获得三个颜色井,“任何”适用于不支持深色模式的旧操作系统,“浅色”和“深色”应该很明显。
以下是一个示例:

Defining a colour set that supports dark mode

这定义了一种颜色,在深色模式下为白色,在浅色模式或旧操作系统上为黑色。
一旦你定义了一个颜色集,你可以在你的draw(_ dirtyRect:)中按如下方式检索颜色:
let strokeColour = NSColor(named: NSColor.Name("gridColour")) ?? NSColor.black

在上述情况下,如果颜色集不存在,则默认为黑色,以处理NSColor(named:)的可选类型。

2

检查暗模式的唯一安全方式是使用以下方法:

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

这是唯一适用于所有情况的解决方案。无论您的视图外观是否混合,或者您允许应用程序使用与系统默认不同的外观,或者您配置系统使用高对比度外观。


这可以修改为检查常规和充满活力。if #available(macOS 10.14, *) { if view.effectiveAppearance.bestMatch(from: [.aqua, .darkAqua]) == .darkAqua || view.effectiveAppearance.bestMatch(from: [.vibrantLight, .vibrantDark]) == .vibrantDark { viewUsesDarkMode = true } else { viewUsesDarkMode = false } } else { viewUsesDarkMode = false } - SouthernYankee65
1
@SouthernYankee65 这是不必要的。bestMatch从提供的选项中选择最接近的外观。在这种情况下,如果effectiveAppearance碰巧是.vibrantDark,则结果仍将是.darkAqua - Jakob Egger

2

2020 | SWIFT 5.1:

方法一:

@Environment(\.colorScheme) var scheme

方法二:

在主题更改的情况下,不会更新SwiftUI。需要实现额外的逻辑来更新视图:

#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 }


只需为 AppleInterfaceThemeChangedNotification 分布式通知添加观察者,然后从那里开始。 - red_menace

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接