Ire
Ire

Reputation: 1361

menu items disabled in macOS menubar App

I'm trying to build a menubar app on macOS.

I can't seem to figure out why some menu items are disabled... Screenshot:

Screenshot of the app

As you can see, the Quit menu item is enabled, and quits the app when clicked. The Preferences item is disabled however.

My code

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

Answers (1)

Ken Thomases
Ken Thomases

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

Related Questions