As of OS X 10.10 most of NSStatusItem has been deprecated in favour of the button
property, which consists of an NSStatusBarButton. It should work like a norma
TL;DR: Any NSButton
instance (with NSImage
that has template=YES
) inside NSStatusItem
property visually looks exactly like NSStatusBarButton
. You can control their highlight
property as you want.
If you want to control highlight of your NSStatusItem
manually and at the same time get all appearance benefits from using NSStatusBarButton
you can use slightly different approach.
You can create your own NSButton
instance which has its own property highlight
that is completely under your control. Then you have to create NSImage
instance and set its template
property to YES
. Then you have to add this button
to [NSStatusItem view]
(yes, which is softly deprecated
) or even as subview to system created [NSStatusItem button]
. After this you have to draw background of NSStatusItem
manually with [NSStatusItem drawStatusBarBackgroundInRect:withHighlight:]
(which is also deprecated, oh).
With this approach, you can combine full control of look and feel of your NSStatusItem
and get automatic styling of button's image.
If you schedule the highlighting of the button in for a subsequent execution on the main thread everything seems to work out. This does work on El Capitan as well.
if self.appPopover.shown {
self.appPopover.performClose(sender)
} else {
if let button = statusItem.button {
// set the button's highlighted property to true
dispatch_async(dispatch_get_main_queue(), {
button.highlighted = true
})
appPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: NSRectEdge.MinY)
}
}
This does work, but you might notice a little flicker due to the button state being changed from ON to OFF and then ON again. The OFF happens because of the popover display. So, to fix that, just move the appPopover.showRelativeToRect() call inside the dispatch_async() block, right after setting the button highlighted.
One idea is swizzling isHighlighted
of NSStatusBarButtonCell
and return an arbitrary value.
https://gist.github.com/questbeat/3244601c8dc779484076
But the problem is whether Apple's review team allows this approach or not...
Luke’s answer is great. I’m just sharing my implementation based on that.
NSButton *button = [[NSButton alloc] initWithFrame:self.statusItem.button.frame];
button.alphaValue = 0;
NSArray *array = @[self.statusItem.button, button];
self.statusItem.button.superview.subviews = array;
[button sendActionOn:(NSLeftMouseDownMask | NSRightMouseDownMask)];
button.target = self;
button.action = @selector(statusItemClicked);
Swift 3 version of Manfred Urban's answer. Works on El Capitan.
extension NSStatusBarButton {
public override func mouseDown(_ event: NSEvent) {
if (event.modifierFlags.contains(NSControlKeyMask)) {
self.rightMouseDown(event)
return
}
self.highlight(true)
(self.target as? TrivialTargetClass)?.togglePopover()
}
}
Don't forget to set the buttons highlight property to false again if appropriate.
Here is one more option. Don't set NSStatusItem
's action
property. Instead add a local event monitor:
[NSEvent addLocalMonitorForEventsMatchingMask:(NSLeftMouseDown | NSRightMouseDown)
handler:^NSEvent *(NSEvent *event) {
if (event.window == self.statusItem.button.window) {
[self itemClicked];
return nil;
}
return event;
}];
Then in -itemClicked
highlight the button using highlight:
method:
- (void)itemClicked {
[self.statusItem.button highlight:YES];
// Do other stuff
}
To unhighlight just call button's highlight:NO
where you need.