swiftuimenubarextra

Handle click on MenuBarExtra's icon without opening menu


Is there a way for MenuBarExtra to handle click on the icon and perform some action, without opening a view?

I know it's possible with NSStatusItem, but I already have other MenuBarExtra items that I would like to keep an order (doesn't seem like there is a way to reorder them either).


Solution

  • Here is a rather convoluted way to do this. Use a .window style menu bar extra, and use a NSViewControllerRepresentable as the window's content.

    In the NSViewController, you can override viewWillAppear to detect the click. Then dismiss the window immediately.

    struct MyView: NSViewControllerRepresentable {
        @Environment(\.dismiss) var dismiss
        
        let onClick: () -> Void
        
        class ChangeDetector: NSViewController {
            var dismiss: DismissAction?
            var onClick: (() -> Void)?
            
            override func viewWillAppear() {
                super.viewWillAppear()
                onClick?()
                Task {
                    dismiss?()
                }
            }
    
            override func loadView() {
                self.view = NSView()
            }
        }
        
        func makeNSViewController(context: Context) -> ChangeDetector {
            let detector = ChangeDetector()
            return detector
        }
        
        func updateNSViewController(_ nsView: ChangeDetector, context: Context) {
            nsView.dismiss = dismiss
            nsView.onClick = onClick
        }
    }
    
    MenuBarExtra("Some title", systemImage: "circle.fill") {
        MyView() {
            print("Clicked!")
        }
    }
    .menuBarExtraStyle(.window)
    

    As for ordering NSStatusItems, note that the user can manually drag the status items while holding down the command key to reorder them.