NSStatusItem изменить изображение для темного оттенка

С выпуском OSX 10.10 beta 3 компания Apple выпустила вариант с темным оттенком. К сожалению, это также означает, что почти все значки в строке состояния (за исключением Apple и Path Finder, которые я видел), включая мою, остаются темными на темном фоне. Как я могу предоставить альтернативное изображение, когда применяется темный оттенок?

Я не вижу изменения API на NSStatusBar или же NSStatusItem это показывает мне изменение, я предполагаю, что это уведомление или что-то реактивное, чтобы легко внести изменения, когда пользователь изменяет оттенок.

Текущий код для рисования изображения заключен в NSView:

- (void)drawRect:(NSRect)dirtyRect
{
    // set view background color
    if (self.isActive) {
        [[NSColor selectedMenuItemColor] setFill];
    } else {
        [[NSColor clearColor] setFill];
    }

    NSRectFill(dirtyRect);

    // set image
    NSImage *image = (self.isActive ? self.alternateImage : self.image);
    _imageView.image = image;
}

6 ответов

Решение

TL;DR: Вам не нужно делать ничего особенного в Dark Theme. Дайте NSStatusItem (или NSStatusBarButton) шаблонное изображение, и оно будет правильно оформлять его в любом контексте меню.


Причина, по которой некоторые элементы состояния приложений (например, PathFinder) уже работают в Dark Theme, заключается в том, что они не устанавливают свое собственное настраиваемое представление в StatusItem, а только задают изображение шаблона в StatusItem.

Что-то вроде:

_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
NSImage *image = [NSImage imageNamed:@"statusItemIcon"];
[image setTemplate:YES];
[_statusItem setImage:image];

Это работает точно так, как вы ожидаете в Mavericks и более ранних версиях, а также в Yosemite и любых будущих выпусках, потому что это позволяет AppKit выполнять всю стилизацию изображения в зависимости от состояния элемента состояния.

Mavericks

В Mavericks (и ранее) было только 2 уникальных стиля предметов. Не в прессе, а в прессе. Эти два стиля в значительной степени выглядели чисто черными и чисто белыми соответственно. (На самом деле "чисто черный" не совсем правильно - был небольшой эффект, который заставлял их выглядеть немного вставленными).

Поскольку было только два возможных состояния, приложения строки состояния могли устанавливать свой собственный вид и легко получать тот же внешний вид, просто рисуя черный или белый в зависимости от их выделенного состояния. (Но опять же обратите внимание, что он не был чисто черным, поэтому приложения должны были либо создавать эффект на изображении, либо удовлетворяться едва заметным значком неуместности).

Yosemite

В Йосемити есть как минимум 32 уникальных стиля предметов. Unpressed in Dark Theme - только один из них. Не существует практического (или непрактичного) способа, которым приложение могло бы создавать собственные стили элементов и иметь их правильное отображение во всех контекстах.

Вот примеры шести из этих возможных стилей:

Шесть возможных стилей элемента статуса

Элементы статуса на неактивной строке меню теперь имеют определенный стиль, в отличие от простого изменения непрозрачности, как в прошлом. Отключенный внешний вид является еще одним возможным вариантом; Есть и другие дополнительные измерения этой матрицы возможностей.

API

Произвольные представления, установленные как NSStatusItem's view свойство не может захватить все эти варианты, поэтому оно (и другие связанные API) устарело в 10.10.

Однако в seed 3 вводится новый API для NSStatusItem:

@property (readonly, strong) NSStatusBarButton *button NS_AVAILABLE_MAC(10_10);

Этот кусок API имеет несколько целей:

  1. Теперь приложение может получить положение на экране (или показать всплывающее окно) элемента состояния, не настраивая собственное пользовательское представление.
  2. Устраняет необходимость в API как image, title, sendActionOn: на NSStatusItem.
  3. Предоставляет класс для нового API: т.е. looksDisabled, Это позволяет приложениям получать стандартный стиль отключения / выключения (например, Bluetooth/Time Machine, когда он выключен), не требуя специального изображения.

Если есть что-то, что не может быть сделано с текущим (не настраиваемым представлением) API, пожалуйста, отправьте запрос на расширение для этого. StatusItems должны обеспечивать поведение или внешний вид в соответствии со стандартом для всех элементов состояния.


Больше обсуждения в https://devforums.apple.com/thread/234839, хотя я суммировал большинство всего здесь.

Я в конечном итоге сделал что-то вроде следования моим обычным перетаскиванию NSStatusItemView: (Используя Swift)

var isDark = false

func isDarkMode() {
    isDark = NSAppearance.currentAppearance().name.hasPrefix("NSAppearanceNameVibrantDark")
}

override func drawRect(dirtyRect: NSRect) {
    super.drawRect(dirtyRect)
    isDarkMode()
    // Now use "isDark" to determine the drawing colour.
    if isDark {
        // ...
    } else {
        // ...
    }
}

Когда пользователь изменил тему в системных настройках, NSView будет вызван системой для повторного рисования, вы можете изменить цвет значка соответственно.

Если вы хотите настроить другой пользовательский интерфейс за пределами этого представления, вы можете использовать KVO для наблюдения за isDark ключ зрения или сделать это самостоятельно.

Новейший метод шаблонов изображений Swift Code Set здесь:

// Insert code here to initialize your application
if let button = statusItem.button {
    button.image = NSImage(named: "StatusIcon")
    button.image?.isTemplate = true  // Just add this line
    button.action = #selector(togglePopover(_:))
}

Тогда это изменит изображение в темном режиме.

Я создал базовую оболочку для NSStatusItem, которую вы можете использовать для поддержки 10.10 и более ранних версий с настраиваемыми представлениями в строке состояния. Вы можете найти его здесь: https://github.com/noahsmartin/YosemiteMenuBar. Основная идея состоит в том, чтобы нарисовать пользовательский вид в NSImage и использовать это изображение как изображение шаблона для элемента строки состояния. Эта оболочка также перенаправляет события кликов в пользовательское представление, поэтому они могут обрабатываться так же, как и до 10.10. Проект содержит базовый пример того, как YosemiteMenuBar может использоваться с настраиваемым представлением в строке состояния.

Но на тот случай, если вы захотите отслеживать изменения статуса, вы можете. Я также знаю, что есть лучший способ определить облегченный / темный режим, чем то, что было сказано выше, но я могу вспомнить это прямо сейчас.

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

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

Когда ваше приложение нарисовало какой-либо элемент GUI, вы можете получить его внешний вид через [NSAppearance currentAppearance] который сам по себе имеет name свойство, которое содержит что-то вроде

NSAppearanceNameVibrantDark->NSAppearanceNameAqua->NSAppearanceNameAquaMavericks

Первая часть - это имя внешнего вида, которое также доступно в виде константы в NSAppearanceNameVibrantDark или же NSAppearanceNameVibrantLight,

Я не знаю, есть ли способ, чтобы получить только первую часть, но я думаю, что это делает трюк на данный момент.

Пример кода:

-(void)awakeFromNib {
    NSStatusItem* myStatusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
    myStatusItem.title = @"Hello World";

    if ([[[NSAppearance currentAppearance] name] containsString:NSAppearanceNameVibrantDark]) {
        myStatusItem.title = @"Dark Interface";
    } else {
        myStatusItem.title = @"Light Interface";
    }
}
Другие вопросы по тегам