メニューバーアプリ開発のTipsです.
マルチディスプレイの状態でMacのメニューバーを眺めていると,非アクティブなディスプレイでは基本的にアイコンが色を失ってグレーアウトするのですが,たまにそのお約束を無視している子がいます.
Siriのアイコンとか,LINEのアイコンとかです.
これがどうやってるのか気になって調べたり検証したところ,やり方そのものは簡単なのですが,情報が全然なかったので記しておきます.
ポイント
- NSStatusBarButton.imageにアイコンを設定するだけだとグレーアウトする
- NSStatusBarButtonにNSViewをaddSubviewしてその中でdrawするとグレーアウトしない
- NSStatusBarButton.imageにアイコンを設定しないとボタンの大きさや位置が定まらない
- NSStatusItem.lengthで幅を設定することもできるけれどあまり賢い方法ではない
解決法
- NSStatusBarButton.imageにアイコンサイズと同じサイズの透明なNSImageを設定する
- 独自NSViewを用意して,アイコンNSImageとNSStatusBarButtonの大きさを渡す
- NSStatusBarButtonに独自NSViewをaddSubViewしてマージンを計算して配置する
サンプルソース
AppDelegate
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
var button: NSStatusBarButton!
func applicationDidFinishLaunching(_ aNotification: Notification) {
button = statusItem.button
button.image = NSImage(size: NSSize(width: 18.0, height: 18.0))
let icon = NSImage(imageLiteralResourceName: "SampleIcon")
let iconView = IconView(icon, button.bounds.size)
button.addSubview(iconView)
}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
}
IconView
import Cocoa
class IconView: NSView {
var icon: NSImage?
init(_ icon: NSImage, _ size: CGSize) {
super.init(frame: NSRect(x: 0.5 * (size.width - 18.0),
y: 0.5 * (size.height - 18.0),
width: 18.0, height: 18.0))
self.icon = icon
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ dirtyRect: NSRect) {
icon?.draw(in: NSRect(x: 0.0, y: 0.0, width: 18.0, height: 18.0))
}
}
サンプルの結果
ちゃんと非アクティブなディスプレイでも色を保っています.
ちなみに,メニューバーの高さ上限は22ポイントですが,22ポイントのアイコンだと窮屈なので,高さは18ポイントが良いです.横幅には制限がないです.(18ポイントということは,最近のMacはRetinaディスプレイなので綺麗に表示するなら2倍の36ピクセルが必要です.)
蛇足
NSStatusBarButton.titleに顔文字を指定しても色がついたままになります(色褪せますが).