You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在SwiftUI的MenuBarExtra中添加右键菜单选项?

SwiftUI实现状态栏图标右键菜单解决方案

在SwiftUI的MenuBarExtra组件中,默认仅支持左键点击展开菜单。要实现右键点击状态栏图标弹出设置菜单的功能,需要通过桥接AppKit的NSView和NSMenu来实现,以下是完整的解决方案:

实现步骤

  • 自定义NSViewRepresentable视图,包装状态栏图标并监听左右键点击事件
  • 在MenuBarExtra中使用自定义视图作为标签,绑定左键点击的菜单展开逻辑
  • 为右键点击创建自定义NSMenu,并与AppSettings状态绑定

完整代码

import SwiftUI
import AppKit

class AppSettings: ObservableObject {
    @Published var sendText = true {
        didSet {
            if !sendText {
                playSound = true
            }
        }
    }
    @Published var playSound = true {
        didSet {
            if !playSound {
                sendText = true
            }
        }
    }
}

// 自定义状态栏视图,处理左右键点击事件
struct StatusBarView: NSViewRepresentable {
    @ObservedObject var settings: AppSettings
    var leftClickAction: () -> Void
    
    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        view.wantsLayer = true
        view.frame = NSRect(x: 0, y: 0, width: 22, height: 22)
        
        // 监听右键点击
        let rightClickGesture = NSClickGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.showRightMenu))
        rightClickGesture.buttonMask = 0x2 // 右键掩码
        view.addGestureRecognizer(rightClickGesture)
        
        // 监听左键点击,触发原菜单展开
        let leftClickGesture = NSClickGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleLeftClick))
        leftClickGesture.buttonMask = 0x1 // 左键掩码
        view.addGestureRecognizer(leftClickGesture)
        
        // 添加状态栏图标(可替换为自定义图标)
        let imageView = NSImageView(image: NSImage(systemSymbolName: "app", accessibilityDescription: "App")!)
        imageView.frame = view.bounds
        view.addSubview(imageView)
        
        return view
    }
    
    func updateNSView(_ nsView: NSView, context: Context) {}
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject {
        let parent: StatusBarView
        
        init(_ parent: StatusBarView) {
            self.parent = parent
        }
        
        @objc func handleLeftClick() {
            parent.leftClickAction()
        }
        
        @objc func showRightMenu() {
            let menu = NSMenu()
            
            // 发送文本开关选项
            let sendTextItem = NSMenuItem(title: "发送文本", action: #selector(toggleSendText), keyEquivalent: "")
            sendTextItem.state = parent.settings.sendText ? .on : .off
            sendTextItem.target = self
            menu.addItem(sendTextItem)
            
            // 播放声音开关选项
            let playSoundItem = NSMenuItem(title: "播放声音", action: #selector(togglePlaySound), keyEquivalent: "")
            playSoundItem.state = parent.settings.playSound ? .on : .off
            playSoundItem.target = self
            menu.addItem(playSoundItem)
            
            // 分隔线与退出选项
            menu.addItem(NSMenuItem.separator())
            let quitItem = NSMenuItem(title: "退出", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
            menu.addItem(quitItem)
            
            // 在当前鼠标位置弹出菜单
            if let event = NSApp.currentEvent {
                NSApp.popUpContextMenu(menu, with: event, for: parent.makeNSView(context: Context()))
            }
        }
        
        @objc func toggleSendText() {
            parent.settings.sendText.toggle()
        }
        
        @objc func togglePlaySound() {
            parent.settings.playSound.toggle()
        }
    }
}

@main
struct SudoApp: App {
    @StateObject var settings = AppSettings()
    @State private var isMenuOpen = false
    
    var body: some Scene {
        MenuBarExtra(isInserted: $isMenuOpen) {
            // 左键点击显示的菜单内容(保留原有逻辑)
            Button(action:{}) { 
                HStack {
                    Image(systemName: "alarm")
                    Text("Toggle")
                    Spacer()
                }
            }
        } label: {
            // 使用自定义视图替代默认标题,处理点击事件
            StatusBarView(settings: settings) {
                isMenuOpen.toggle()
            }
        }
    }
}

关键说明

  • 通过NSViewRepresentable实现SwiftUI与AppKit的桥接,直接处理系统级鼠标事件
  • 右键菜单使用AppKit的NSMenu创建,支持与AppSettings状态绑定,实现开关实时同步
  • 左键点击保留原有MenuBarExtra的菜单展开逻辑,保证功能兼容

内容的提问来源于stack exchange,提问作者alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 10:53:16