macOS单个菜单项类NSMenuItem
macOS单个菜单项类NSMenuItem

macOS单个菜单项类NSMenuItem

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/

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注