macOS Status Bar apps: Submenus
April 2, 2023 | Previous part | Next part |
Submenus in Swift
To create a submenu, you will be creating an NSMenuItem
and adding a submenu to it. This submenu is another instance of an NSMenu
, which you then add items to. In part 1, we added NSMenuItems
to the NSMenu
using the .addItem
method, seen here:
statusBarMenu.addItem(
withTitle: "hi",
action: #selector(AppDelegate.hello),
keyEquivalent: "h"
)
However, we did not explicitly say we were adding NSMenuItems, we added them using the NSMenuItem initializer:
init(title: String, action: Selector?, keyEquivalent: String)
Architecturally It may look like this:
- mainMenuBar = NSMenu()
- subMenuBar = NSMenuItem()
- - subMenuBar.title = "Main first"
- - subMenuBar.submenu = NSMenu()
- - subMenuBar.submenu?.addItem(NSMenuItem(withTitle: "first", action: #selector(), keyEquivalent: "")
- - subMenuBar.submenu?.addItem(NSMenuItem(withTitle: "second", action: #selector(), keyEquivalent: "")
- - subMenuBar.submenu?.addItem(NSMenuItem(withTitle: "third", action: #selector(), keyEquivalent: "")
- mainMenuBar.addItem(subMenuBar)
- mainMenuBar.addItem(NSMenuItem(withTitle: "Main second", action: nil, keyEquivalent: ""))
- mainMenuBar.addItem(NSMenuItem(withTitle: "Main third", action: nil, keyEquivalent: ""))
If you do set up a submenu, you can also insert items into it by referring to the index of the menu item. You can also choose to insert into a position explicitly like so:
statusBarMenu.item(at: 0)?
.submenu?.insertItem(
withTitle: "Sub-m",
action: nil,
keyEquivalent: "h",
at: 3
)
Code example
import Cocoa
import SwiftUI
@main
class AppDelegate: NSObject, NSApplicationDelegate {
let statusBarMenu = NSMenu()
var statusBarItem: NSStatusItem!
func applicationDidFinishLaunching(_ aNotification: Notification) {
let statusBar = NSStatusBar.system
statusBarItem = statusBar.statusItem(
withLength: NSStatusItem.variableLength
)
statusBarItem.button?.image = NSImage(named: NSImage.Name("NXdefaulticon"))
statusBarItem.menu = statusBarMenu
let vmStatusMenu = NSMenuItem()
vmStatusMenu.title = "VM Status"
vmStatusMenu.image = NSImage(named: "NSAppleMenuImage")
vmStatusMenu.submenu = NSMenu()
vmStatusMenu.submenu?.addItem(NSMenuItem(title: "Start", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: ""))
vmStatusMenu.submenu?.addItem(NSMenuItem(title: "Stop", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: ""))
vmStatusMenu.submenu?.addItem(NSMenuItem(title: "Restart", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: ""))
vmStatusMenu.submenu?.addItem(NSMenuItem.separator())
vmStatusMenu.submenu?.addItem(NSMenuItem(title: "About the app", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: ""))
statusBarMenu.addItem(vmStatusMenu)
statusBarMenu.addItem(
withTitle: "Settings",
action: #selector(AppDelegate.hello),
keyEquivalent: ","
)
statusBarMenu.addItem(
withTitle: "Quit",
action: #selector(AppDelegate.quit),
keyEquivalent: "q"
)
}
@objc func hello() {
print("Hi world")
}
@objc func quit() {
print("Shutting down")
NSApplication.shared.terminate(self)
}
}
func applicationWillTerminate(_ aNotification: Notification) {}
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { true }