Reputation: 1361
I'm trying to build a menubar app on macOS.
I can't seem to figure out why some menu items are disabled... Screenshot:
As you can see, the Quit menu item is enabled, and quits the app when clicked. The Preferences item is disabled however.
AppDelegate.swift:
let menuBarItem = NSStatusBar.system().statusItem(withLength: NSSquareStatusItemLength)
func applicationDidFinishLaunching(_ aNotification: Notification) {
menuBarItem.button?.image = NSImage(named: "MenuBarIcon")
menuBarItem.menu = MenuBarMenu()
}
MenuBarMenu.swift:
class MenuBarMenu: NSMenu {
init() {
super.init(title: "Menu")
self.addItem(withTitle: "Preferences...", action: #selector(MenuBarActions.openPreferencesWindow(_:)), keyEquivalent: "")
self.addItem(NSMenuItem.separator())
self.addItem(withTitle: "Quit", action: #selector(MenuBarActions.terminate(_:)), keyEquivalent: "")
}
required init(coder decoder: NSCoder) {
fatalError("init(coder:) has not been impemented")
}
}
class MenuBarActions {
@objc static func terminate(_ sender: NSMenuItem) {
NSApp.terminate(sender)
}
@objc static func openPreferencesWindow(_ sender: NSMenuItem) {
print("preferences")
}
}
I am using the exact same way to create both MenuBarItems and the same structure for the selectors, so I am a little confused by this inconsistency. What is the reason this happens and how can I solve this problem?
Upvotes: 5
Views: 1921
Reputation: 90521
Your Quit menu item is working "accidentally". It is not using the terminate(_:)
method you implemented. Put a print()
statement there and you'll see it's not being invoked.
A menu item either has a specific target object assigned or it uses the responder chain to search for an appropriate target. You are not assigning a target for your menu items, so they are using the responder chain. Your MenuBarActions
class is not part of the responder chain. (Classes generally can't be. Certain objects can be.) So, the menu items will never target your class.
The Quit menu works because the application object is on the responder chain and it has a terminate(_:)
method. In fact, that's what your terminate(_:)
method would invoke if it were ever being called. But the menu item is actually invoking it directly.
You should create an actual controller object (not just class) that implements the action methods and set the menu items' target
property to it.
Upvotes: 5