26

在 OSX 10.10 beta 3 中,Apple 发布了他们的深色选项。不幸的是,这也意味着几乎所有的状态栏图标(除了我见过的 Apple 和 Path Finder 的图标),包括我的,在深色背景上仍然是深色的。如何在应用深色时提供替代图像?

我没有看到 API 更改NSStatusBarNSStatusItem显示更改,我假设它是通知或反应性的东西,以便在用户更改色调时轻松进行更改。

当前绘制图像的代码包含在一个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;
}
4

6 回答 6

62

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(和更早的版本)中,只有 2 种独特风格的物品。未按和按。这两种风格几乎分别看起来是纯黑色和纯白色。(实际上“纯黑色”并不完全正确——有一个小效果使它们看起来有点内嵌)。

因为只有两种可能的状态,所以状态栏应用程序可以设置自己的视图,并根据其突出显示的状态只需绘制黑色或白色即可轻松获得相同的外观。(但请再次注意,它不是纯黑色,因此应用程序要么必须在图像中构建效果,要么满足于几乎不明显的不合适的图标)。

优胜美地

在优胜美地,至少有 32 种独特的物品样式。Unpressed in Dark Theme 只是其中之一。没有任何实用(或不实用)的方法可以让应用程序自行设置项目样式并使其在所有情况下看起来都正确。

以下是其中六种可能样式的示例:

六种可能的状态项样式

非活动菜单栏上的状态项现在具有特定的样式,而不是像过去那样简单的不透明度更改。残疾外观是另一种可能的变化;这个可能性矩阵还有其他额外的维度。

API

设置为 NSStatusItemview属性的任意视图无法捕获所有这些变化,因此它(和其他相关 API)在 10.10 中已弃用。

然而,种子 3 在 NSStatusItem 上引入了新的 API:

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

这段 API 有几个目的:

  1. 应用程序现在可以在不设置自己的自定义视图的情况下获取状态项的屏幕位置(或显示弹出框)。
  2. 在 NSStatusItem 上消除对 API 的需要,如image, title, 。sendActionOn:
  3. 为新 API 提供一个类:即looksDisabled. 这允许应用程序获得标准的禁用/关闭样式(如关闭时的蓝牙/Time Machine),而无需自定义图像。

如果当前(非自定义视图)API 无法完成某些操作,请提交增强请求。StatusItems 应该以它在所有状态项中标准化的方式提供行为或外观。


更多讨论请参见 https://devforums.apple.com/thread/234839,尽管我已经在这里总结了大部分内容。

于 2014-07-09T03:05:50.713 回答
6

我最终对我的自定义拖放做了类似的事情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 {
        // ...
    }
}

当用户在 System Preferences 中更改 Theme 时,NSView会被系统调用重新绘制,您可以相应地更改图标颜色。

如果您希望在此视图之外调整其他自定义 UI,您可以使用 KVO 来观察isDark视图的键或自己进行。

于 2014-07-09T16:31:05.843 回答
2

我围绕 NSStatusItem 创建了一个基本包装器,您可以使用它为 10.10 及更早版本提供支持,并在状态栏中使用自定义视图。你可以在这里找到它:https ://github.com/noahsmartin/YosemiteMenuBar 基本思想是将自定义视图绘制成一个 NSImage 并将此图像用作状态栏项的模板图像。此包装器还将单击事件转发到自定义视图,因此可以像 10.10 之前一样处理它们。该项目包含一个基本示例,说明如何将 YosemiteMenuBar 与状态栏上的自定义视图一起使用。

于 2014-07-29T06:39:35.553 回答
2

最新的swift代码集图像模板方法在这里:

// 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(_:))
}

然后它会在黑暗模式下改变图像。

于 2018-10-22T03:59:38.243 回答
1

当您的应用程序绘制了任何 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";
    }
}
于 2014-07-08T13:26:33.793 回答
1

但是以防万一您确实想监视状态更改。我也知道有比上面所说的更好的方法来确定 lite/dark 模式,但我现在可以记住它。

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

//
-(void) themeChange :(NSNotification *) notification
{
    NSLog (@"%@", notification);
}
于 2016-02-03T00:27:15.913 回答