macOS SwiftUI: Dropdown Menu in a MenuExtraApp not working?

I have included sample code below. In my MenuExtraView, I've added a dropdown Menu to access a quit option. When I render this view as the application's main window, everything works fine. But when the view is used as an item in the NSStatusItem.menu, the dropdown doesn't work despite the view being rendered correctly.

Any ideas or things I'm simply doing wrong here?

I know xcode 14 will include MenuBarExtra directly in SwiftUI, however, I'm currently targeting macos 12.

import AppKit
import SwiftUI

@main
struct MenuExtraApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    var body: some Scene {
        WindowGroup {
            MenuExtraView()
//          EmptyView().frame(width: .zero)
        }
    }
}

struct MenuExtraView: View {
    var body: some View {
        VStack(alignment: .leading) {
            HStack(alignment: .center) {\
                VStack(alignment: .leading) {
                    Text(verbatim: "Hello World").font(.subheadline)
                }
                Spacer()
                Menu {
                    Button("Quit") {
                        NSApplication.shared.terminate(nil)
                    }.keyboardShortcut("q")
                } label: {
                    Image(systemName: "gearshape").imageScale(.large)
                }
                .menuStyle(BorderlessButtonMenuStyle())
                .menuIndicator(.hidden)
                .fixedSize()
            }.padding()
            Spacer()
        }
    }
}

class AppDelegate: NSObject, NSApplicationDelegate {
    var statusItem: NSStatusItem?

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let hostingView = NSHostingView(rootView: MenuExtraView())
        hostingView.frame = NSRect(x: 0, y: 0, width: 350, height: 400)

        let menuItem = NSMenuItem()
        menuItem.view = hostingView

        let menu = NSMenu()
        menu.addItem(menuItem)

        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)

        statusItem?.menu = menu
        statusItem?.button?.image = NSImage(systemSymbolName: "gearshape.circle", accessibilityDescription: "Logo")
    }
}
Accepted Answer

Controls, like menus and pickers with the menu style, don't work when placed in a view of an NSMenuItem. I believe only simple controls and ones with button-like behaviour do.


If you need the menu as it is, and because it would be classed as a complex view, I would recommend using a popover instead. Something like this should work:

var statusItem: NSStatusItem?
var popover: NSPopover?

func applicationDidFinishLaunching(_ aNotification: Notification) {
    statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
    statusItem?.button?.image = NSImage(systemSymbolName: "gearshape.circle", accessibilityDescription: "Logo")
    statusItem?.button?.action = #selector(showPopover)

    let hostingViewController = NSHostingController(rootView: MenuExtraView())

    popover = NSPopover()
    popover?.contentViewController = hostingViewController
    popover?.contentSize = .init(width: 350, height: 400)
    popover?.behavior = .transient
}

@objc func showPopover(_ button: NSButton) {
    popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
}


If you just need a standard menu for your status item instead of a view, then add an item like this:

menu.addItem(withTitle: "Quit", action: #selector(NSApp.terminate), keyEquivalent: "q")

I forgot to add this, but when you do start to target macOS Ventura, menus and pickers will work properly in a MenuBarExtra with the .window style.

macOS SwiftUI: Dropdown Menu in a MenuExtraApp not working?
 
 
Q