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 }