如何在 macOS 10.14 上检测到暗模式?

How can dark mode be detected on macOS 10.14?

在 macOS 10.14 中,用户可以选择采用系统范围的浅色或深色外观,我需要根据当前模式手动调整一些颜色。

系统是10.14的我已经用当前外观检查了

+ (BOOL)isDarkMode {
    NSAppearance *appearance = NSAppearance.currentAppearance;
    if (@available(*, macOS 10.14)) {
        return appearance.name == NSAppearanceNameDarkAqua;
    }

    return NO;
}

检测视图模式变化的方法是:

- (void)updateLayer;
- (void)drawRect:(NSRect)dirtyRect;
- (void)layout;
- (void)updateConstraints;

检测视图控制器模式变化的方法是:

- (void)updateViewConstraints;
- (void)viewWillLayout;
- (void)viewDidLayout;

使用通知:

// Monitor menu/dock theme changes...
[NSDistributedNotificationCenter.defaultCenter addObserver:self selector:@selector(themeChanged:) name:@"AppleInterfaceThemeChangedNotification" object: nil];

-(void)themeChanged:(NSNotification *) notification {
    NSLog (@"%@", notification);
}

更多信息Dark Mode Documentation

由于您通常通过 effectiveAppearance 获得的实际外观对象是复合外观,因此直接询问其名称可能不是一个可靠的解决方案。

询问 currentAppearance 通常也不是一个好主意,因为视图可能被明确设置为浅色模式,或者您想知道视图在 [= 之外是浅色还是深色13=] 模式切换后可能会得到错误结果的地方。

我想出的解决方案是这样的:

BOOL appearanceIsDark(NSAppearance * appearance)
{
    if (@available(macOS 10.14, *)) {
        NSAppearanceName basicAppearance = [appearance bestMatchFromAppearancesWithNames:@[
            NSAppearanceNameAqua,
            NSAppearanceNameDarkAqua
        ]];
        return [basicAppearance isEqualToString:NSAppearanceNameDarkAqua];
    } else {
        return NO;
    }
}

您可以像 appearanceIsDark(someView.effectiveAppearance) 一样使用它,因为如果您明确设置 someView.appearance.

,则特定视图的外观可能与另一个视图的外观不同

你也可以在NSAppearance上创建一个类别,然后添加一个- (BOOL)isDark方法来获取someView.effectiveAppearance.isDark(最好选择一个以后不太可能被Apple使用的名称,例如,通过添加供应商前缀)。

Swift 4

func isDarkMode(view: NSView) -> Bool {
    if #available(OSX 10.14, *) {
        return view.effectiveAppearance.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua
    }
    return false
}

要知道 app 外观是否为深色,请使用下一个代码:

+ (BOOL)isDarkMode {
    NSString *interfaceStyle = [NSUserDefaults.standardUserDefaults valueForKey:@"AppleInterfaceStyle"];
    return [interfaceStyle isEqualToString:@"Dark"];
}

对我来说,如果我想要一个全局状态而不是每个视图,并且我无权访问该视图,并且我希望收到更新通知,那么这些答案都不起作用。

解决方案是在主线程中请求NSApp.effectiveAppearance,或者至少当前回调方法返回给系统后。

所以,首先我必须按照 Saúl Moreno Abril 的指示使用

这样的代码进行注册
[NSDistributedNotificationCenter.defaultCenter addObserver:self selector:@selector(themeChanged:) name:@"AppleInterfaceThemeChangedNotification" object: nil];

然后在回调方法上写类似

-(void)themeChanged:(NSNotification *) notification {
    [self performSelectorOnMainThread:@selector(themeChangedOnMainThread) withObject:nil waitUntilDone:false];
}

然后是实际代码:

- (void) themeChangedOnMainThread {
    NSAppearance* appearance = NSApp.effectiveAppearance;
    NSString* name = appearance.name;
    BOOL dark = [appearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]] == NSAppearanceNameDarkAqua;
}

Borzh 的回答也有帮助,但似乎比其他答案更脆弱。

一个视图实际上有8种可能appearances,其中4种是普通使用的。也就是说,

  1. NSAppearanceNameAqua灯光模式,
  2. NSAppearanceNameDarkAqua 深色模式,
  3. NSAppearanceNameAccessibilityHighContrastAqua 增强对比度的灯光模式(从辅助功能设置),
  4. NSAppearanceNameAccessibilityHighContrastDarkAqua 增加对比度的深色模式。

直接比较

appearance.name == NSAppearanceNameDarkAqua;

如果对比度增加,可能无法检测到暗模式。因此,请始终使用 bestMatchFromAppearancesWithNames

最好考虑高对比度外观以获得更好的可访问性。