所谓菜单栏状态项,指的是macOS顶部菜单栏右侧的那一排图标。Windows里叫系统托盘,macOS就叫菜单栏状态项。后面简称状态项。
获取状态项并制定图标
let statusItem = NSStatusBar.system.statusItem(withLength:NSStatusItem.squareLength)
guard let button = statusItem.button else {
fatalError()
}
button.image = NSImage(imageLiteralResourceName: "TrayIcon")
图标的图片选择40x40像素的,放在2x下。
添加响应或菜单项
这里是一个坑。虽然状态项支持单击和显示菜单,但是苹果规定,当存在菜单时,单击无效,而只会显示菜单。
var menu: NSMenu?
When non-nil, the status item’s single click action behavior is not used. The menu can be removed by setting the value of this property to nil.
所以你要么添加button
的点击功能,要么选择构建一个菜单。这个坑可以通过技术手段绕过,我们后面再提。
鼠标左键和鼠标右键对应不同的响应
状态项默认只支持鼠标左键的点击,而不支持其它的鼠标事件。如果想要支持鼠标右键的点击,我们需要自行实现。
https://stackoverflow.com/questions/32188581/call-action-when-nsstatusbarbutton-is-right-clicked
我选择是上面链接中的方案,新建一个自定义的视图,该视图支持鼠标右键点击,然后再将该视图添加到button
上。
自定义的视图:
class MouseRightClickView: NSView {
var closure:(() -> ())!
override func rightMouseUp(with event: NSEvent) {
super.rightMouseUp(with: event)
closure()
}
}
同时支持鼠标单击和菜单项
现在状态项支持鼠标左键和鼠标右键了,下面我们来实现鼠标左键点击“显示/隐藏应用”,右键点击“显示菜单项”。
guard let button = statusItem.button else {
fatalError()
}
button.image = NSImage(imageLiteralResourceName: "TrayIcon")
button.action = #selector(mouseLeftButtonClicked)
// Add mouse right click
let subView = MouseRightClickView(frame: button.frame)
subView.closure = {
self.constructMenu()
button.performClick(nil) // menu won't show without this
}
button.addSubview(subView)
大家注意看,创建完成菜单后,必须手工添加一个按钮的点击。如果没有这个,那么只会生成菜单,而不会立即弹出菜单。最后,由于苹果规定如果存在菜单,则不会响应鼠标点击,我们必须在每次菜单关闭时,自动去掉菜单。这就需要设置菜单的代理。
private func constructMenu() {
let menu = NSMenu()
menu.delegate = self
...
}
菜单代理
extension AppDelegate:NSMenuDelegate {
// remove the menu or later mouse left click will call it.
func menuDidClose(_ menu: NSMenu) {
statusItem.menu = nil
}
}