web-dev-qa-db-fra.com

Comment le mode sombre peut-il être détecté sur macOS 10.14?

Dans macOS 10.14, les utilisateurs peuvent choisir d'adopter une apparence claire ou sombre à l'échelle du système et je dois ajuster manuellement certaines couleurs en fonction du mode actuel.

10
Saúl Moreno Abril

Étant donné que l'objet d'apparence réel que vous obtenez généralement via effectiveAppearance est une apparence composite, demander son nom directement n'est probablement pas une solution fiable.

Demander le currentAppearance n'est généralement pas une bonne idée non plus, car une vue peut être explicitement définie en mode clair ou vous voulez savoir si une vue est claire ou sombre en dehors d'un drawRect: où vous pourriez obtenir des résultats incorrects après un changement de mode.

La solution que j'ai trouvée ressemble à ceci:

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

Vous l'utiliseriez comme appearanceIsDark(someView.effectiveAppearance) car l'apparence d'une vue spécifique peut être différente de celle d'une autre vue si vous définissez explicitement someView.appearance.

Vous pouvez également créer une catégorie sur NSAppearance et ajouter une méthode - (BOOL)isDark pour obtenir someView.effectiveAppearance.isDark (Mieux choisir un nom qui ne sera probablement pas utilisé par Apple dans le futur, par exemple en ajoutant un préfixe de fournisseur).

18
DarkDust

J'ai utilisé l'apparence actuelle pour vérifier si le système est 10,14

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

    return NO;
}

Et pour détecter le changement de mode dans une vue, les méthodes sont les suivantes:

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

Et pour détecter le changement de mode dans un contrôleur de vue, les méthodes sont les suivantes:

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

Utilisation de la notification:

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

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

Pour plus d'informations Documentation du mode sombre

15
Saúl Moreno Abril

Swift 4

func isDarkMode(view: NSView?) -> Bool {
    if #available(OSX 10.14, *) {
        if let appearance = view?.effectiveAppearance ?? NSAppearance.current {
            return (appearance.name == .darkAqua)
        }
    }
    return false
}

MISE À JOUR:

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

Il y a en fait 8 possibles apparences pour une vue, et 4 d'entre eux sont pour un usage ordinaire. C'est,

  1. NSAppearanceNameAqua le Mode Lumière,
  2. NSAppearanceNameDarkAqua le mode sombre,
  3. NSAppearanceNameAccessibilityHighContrastAqua Mode d'éclairage avec contraste accru (défini à partir de l'accessibilité),
  4. NSAppearanceNameAccessibilityHighContrastDarkAqua Mode sombre avec un contraste accru.

Une comparaison directe

appearance.name == NSAppearanceNameDarkAqua;

peut ne pas détecter le mode sombre s'il présente un contraste accru. Donc, utilisez toujours bestMatchFromAppearancesWithNames à la place.

Et il vaut encore mieux prendre en compte les apparences contrastées pour une meilleure accessibilité.

0
L.-T. Chen

Pour savoir si l'apparence app est sombre, utilisez le code suivant:

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

Pour moi, aucune de ces réponses n'a fonctionné, si je voulais un état global, pas par vue, et je n'avais pas accès à la vue, et je voulais être informé des mises à jour.

La solution était de demander NSApp.effectiveAppearance dans le thread principal, ou au moins après la méthode de rappel actuelle est revenue au système.

Donc, je dois d'abord m'inscrire, en suivant les instructions de Saúl Moreno Abril, avec un code comme

[NSDistributedNotificationCenter.defaultCenter addObserver:self selector:@selector(themeChanged:) name:@"AppleInterfaceThemeChangedNotification" object: nil];

puis sur la méthode de rappel écrire quelque chose comme

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

puis le code réel:

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

La réponse de Borzh a également aidé, mais semble plus fragile que les autres.

0
Panayotis