NSMenuItem 是 macOS 中用于构建菜单栏、右键菜单、弹出菜单等的单个菜单项类,它是 AppKit(AppKit.framework)提供的组件之一。
通常配合 NSMenu() 构建完整的状态栏菜单。
例如,Mac应用顶部的菜单,都是 NSMenu 和 NSMenuItem 构成。

基本结构为:
NSApplication.shared.mainMenu
├── NSMenuItem("轻压图片") → submenu: NSMenu(...)
│ ├── NSMenuItem("关于轻压图片") ...
│ ├── NSMenuItem("服务")
│ └── ...
├── NSMenuItem("文件") → submenu: NSMenu(...)
│ ├── NSMenuItem("新建窗口")
│ ├── NSMenuItem("关闭")
│ └── ...
└── ...
每个 NSMenuItem 都可以有一个 submenu(子菜单)。
基本用法
创建菜单项:
let menuItem = NSMenuItem(title: "打开 App", action: #selector(openApp), keyEquivalent: "o")
menuItem.target = self
添加到菜单:
let menu = NSMenu()
menu.addItem(menuItem)
常用属性
1、title:显示的名称,例如 “退出”。
2、action:点击后触发的函数(需要 @objc 方法)。
3、target:响应者对象(方法所在的类)。
指定菜单项点击时,去哪个对象上查找 action 方法。
注意:需要搭配 action: #selector(…) 使用,否则点击无效。
let menuItem = NSMenuItem(title: "打开窗口", action: #selector(openWindow), keyEquivalent: "o")
menuItem.target = self
@objc func openWindow() {
print("点击了打开窗口")
}
如果不设置target,系统会沿着响应链(responder chain)查找能响应 #selector(openWindow) 的对象,可能找不到,导致点击无效。
当然,也有特殊的场景,例如:
let quitItem = NSMenuItem(title: "退出", action: #selector(NSApp.terminate(_:)), keyEquivalent: "q")
menu.addItem(quitItem)
这个NSMenuItem并没有设置target,仍然可以实现菜单功能。
这是因为,系统执行的逻辑为:自动沿“响应链”查找一个可以响应这个 action 的对象,直到找到为止。
对于 #selector(NSApp.terminate(_:)) 这种系统方法,系统能自动识别这是 NSApplication 提供的方法,于是就自动把 target 设置为 NSApp。
因此,如果是自己编写的方法,就需要设置target,如果是NSApp这种可以沿“响应链”查找到响应action的对象,就不需要单独再设置target。
4、keyEquivalent:快捷键(如 “q” 表示 ⌘Q)。
5、isEnabled:是否可点击。
let disabledItem = NSMenuItem(title: "开发中", action: nil, keyEquivalent: "")
disabledItem.isEnabled = false
menu.addItem(disabledItem)
但是实际测试中,发现isEnabled并不能实现不可点击的状态,只有将action改为nil,才能实现不可点击的状态。
let quitItem = NSMenuItem(title: "退出", action: nil, keyEquivalent: "q")

6、state:可选的状态,如 .on / .off(用于打勾)。
控制菜单项前面是否有一个打勾符号(✓),常用于表示开关、选中状态。
可选值:.on、.off和.mixed。
例如:
let darkModeItem = NSMenuItem(title: "启用暗黑模式", action: #selector(toggleDarkMode), keyEquivalent: "d")
darkModeItem.target = self
darkModeItem.state = .on // 初始为选中
@objc func toggleDarkMode(_ sender: NSMenuItem) {
if sender.state == .on {
sender.state = .off
print("关闭暗黑模式")
} else {
sender.state = .on
print("启用暗黑模式")
}
}

7、submenu:可以设置子菜单(NSMenu)形成嵌套结构。
给一个菜单项设置下一级菜单,从而形成下拉嵌套结构。
例如:
let parentItem = NSMenuItem(title: "更多设置", action: nil, keyEquivalent: "")
let submenu = NSMenu()
submenu.addItem(NSMenuItem(title: "字体大小", action: #selector(changeFontSize), keyEquivalent: "f"))
submenu.addItem(NSMenuItem(title: "主题颜色", action: #selector(changeTheme), keyEquivalent: "t"))
parentItem.submenu = submenu
menu.addItem(parentItem)
可以将一个子菜单无限嵌套形成多级结构。

其他方法
1、分割线菜单项:separator()。
let separator = NSMenuItem.separator()
menu.addItem(separator)
实际应用
完整状态栏菜单:
let menu = NSMenu()
let openItem = NSMenuItem(title: "打开 App", action: #selector(openApp), keyEquivalent: "o")
openItem.target = self
menu.addItem(openItem)
menu.addItem(NSMenuItem.separator()) // 添加分割线
let quitItem = NSMenuItem(title: "退出", action: #selector(NSApp.terminate(_:)), keyEquivalent: "q")
menu.addItem(quitItem)
statusItem.menu = menu
详细菜单栏代码示例,可见《macOS顶部和上下文菜单NSMenu》或《macOS状态栏图标(系统右上角)NSStatusBar》文章。
总结
NSMenuItem 是 macOS 中构建菜单栏、弹出菜单、状态栏菜单的“选项单位”,配合 NSMenu 使用,可以构建强大的交互菜单结构。
相关文章
1、macOS顶部和上下文菜单NSMenu:https://fangjunyu.com/2025/06/23/macos%e9%a1%b6%e9%83%a8%e5%92%8c%e4%b8%8a%e4%b8%8b%e6%96%87%e8%8f%9c%e5%8d%95nsmenu/
2、macOS状态栏图标(系统右上角)NSStatusBar:https://fangjunyu.com/2025/06/24/macos%e7%8a%b6%e6%80%81%e6%a0%8f%e5%9b%be%e6%a0%87%ef%bc%88%e7%b3%bb%e7%bb%9f%e5%8f%b3%e4%b8%8a%e8%a7%92%ef%bc%89nsstatusbar/
3、Swift @objc属性声明:https://fangjunyu.com/2025/04/06/swift-objc%e5%b1%9e%e6%80%a7%e5%a3%b0%e6%98%8e/
4、SwiftUI绑定快捷键keyboardShortcut:https://fangjunyu.com/2025/06/19/swiftui%e7%bb%91%e5%ae%9a%e5%bf%ab%e6%8d%b7%e9%94%aekeyboardshortcut/