macOS状态栏图标关键组件NSStatusItem
macOS状态栏图标关键组件NSStatusItem

macOS状态栏图标关键组件NSStatusItem

NSStatusItem 是 macOS 状态栏开发中的核心类,用于在系统顶部状态栏中显示自定义图标,并附加菜单或执行自定义操作。几乎所有显示在状态栏的第三方应用(如 Dropbox、iStat Menus、网易云音乐)都会用到它。

NSStatusItem 是什么?

NSStatusItem 表示状态栏上的一项,它是由系统的 NSStatusBar 创建的对象。不能直接实例化 NSStatusItem,必须通过 NSStatusBar 来创建。

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

创建之后,可以:

1、显示图标(button.image)。

2、添加菜单(menu)。

3、响应点击事件(通过 button.action)。

当程序运行时执行这段NSStatusBar.system.statusItem代码,macOS就会立即在右上角状态栏中添加图标(即使没有使用AppDelegate)。

架构关系图

NSStatusItem示例代码:

private var statusItem: NSStatusItem!

// 创建系统菜单栏图标
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem.button {
    button.image = NSImage(named: "templateIcon")
}

对应的架构关系图:

NSStatusBar(系统状态栏管理器)
   └─ NSStatusItem(状态栏上的图标项)
         └─ NSStatusBarButton(按钮 UI,可设置图标/点击事件)

NSStatusBar:macOS 中用于创建状态栏图标(系统右上角)的 API,很多 App(比如 Dropbox、网易云音乐、Clipy)都使用它来常驻状态栏,并提供快捷操作。

常用属性

1、button:NSStatusBarButton?,可用于设置图标、响应点击。

表示菜单栏上真正可见的图标按钮。

是一个 NSStatusBarButton 对象,继承自 NSButton。

可以对它设置图标、标题、点击事件:

if let button = statusItem.button {
    button.image = NSImage(named: "templateIcon")
    button.action = #selector(handleClick)
    button.target = self
}

注意: 如果设置了 statusItem.menu,则 button.action 将不再生效(互斥)。

2、menu:NSMenu?,用于设置点击图标时显示的菜单。

设置菜单栏图标点击后显示的菜单。

指定一个 NSMenu 对象,当用户点击图标时弹出。

可以包含任意数量的 NSMenuItem,支持分隔符、子菜单等:

let menu = NSMenu()
menu.addItem(NSMenuItem(title: "打开 App", action: #selector(openApp), keyEquivalent: "o"))
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "退出", action: #selector(NSApp.terminate(_:)), keyEquivalent: "q"))
statusItem.menu = menu

如果设置了 menu,则 button.action 不会被触发(因为点击就是弹出菜单)。

3、length:CGFloat,图标的宽度。

可以设置图标按钮的宽度,例如固定为 50:

let statusItem = NSStatusBar.system.statusItem(withLength: 50)

也可以设置100:   

使用 NSStatusItem.variableLength(默认值)会根据图标大小自动适配。

4、statusBar:返回创建此项的 NSStatusBar 实例。

大部分场景都不会主动使用它,主要用于追踪归属。

let parentBar = statusItem.statusBar

5、isVisible:控制图标是否显示。

statusItem.isVisible = false // 隐藏图标
statusItem.isVisible = true  // 重新显示

通常用于动态显示/隐藏图标,而不是完全销毁。

使用示例:

class StatusBarController:ObservableObject {
    private var statusItem: NSStatusItem!
    
    init() {
        // 创建系统菜单栏图标
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        if let button = statusItem.button {
            button.image = NSImage(named: "templateIcon")
        }
        
        // 创建菜单
        let menu = NSMenu()
        let hideItem = NSMenuItem(title: "隐藏 App", action: #selector(hideApp), keyEquivalent: "o")
        hideItem.target = self
        menu.addItem(hideItem)
        statusItem.menu = menu
    }
    
    @objc func hideApp(_ sender: Any?) {
        print("隐藏 App")
        statusItem.isVisible = false
    }
}

注意,isVisible = false在某些macOS版本上隐藏菜单后,可能重启或构建仍然无法恢复,因此只能在初始化中重新设置iSVisible = true,问题请见《macOS状态栏不显示图标的问题》。

6、autosaveName:给状态项命名以支持自动保存状态(如位置)。

在 macOS 支持菜单栏项目可拖拽排序时,这个字段用于标识每个图标项。

statusItem.autosaveName = NSStatusItem.AutosaveName("com.yourapp.menuitem")

如果设置了,它可以让系统记住上次用户拖动后的顺序。

7、NSStatusBar.system.removeStatusItem(item):移除图标项。

NSStatusBar.system.removeStatusItem(statusItem)

使用这个方法彻底移除菜单栏图标,不再显示。

调用后,原有的 statusItem 不能再使用,最好设为 nil。

注意事项

1、图标图像要使用模板图像(Template Image)

用于适配浅色/深色模式。

在 .xcassets 中设置 “Render As: Template Image”。

macOS状态栏图标推荐尺寸是 18×18 pt(对应 36×36 px @2x)。如果图片太大/太小/没有内边距,可能不会显示或显示异常。

图标配置的内容,详情请见《macOS状态栏图标(系统右上角)NSStatusBar》的设置状态栏图标部分。

2、menu 与 action 二选一

如果设置了 statusItem.menu,点击图标会弹出菜单,不会触发 button.action。

如果想响应点击而非弹出菜单,就不要设置 menu,而是设置 button.action。

// 创建系统菜单栏图标
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
if let button = statusItem.button {
    button.image = NSImage(named: "templateIcon")
    button.action = #selector(statusBarButtonClicked)   // 1、想实现Button的 action 方法,就不能设置menu
    button.target = self    // 和 action 配合使用,不实现 Action 方法,就不用配置 target。
}

// 如果需要实现 Button 的 action 方法,下面的创建菜单部分就可以删除/隐藏
// 创建菜单
let menu = NSMenu()

let openItem = NSMenuItem(title: "打开 App", action: #selector(openApp), keyEquivalent: "o")
openItem.target = self
menu.addItem(openItem)
menu.addItem(NSMenuItem.separator())
statusItem.menu = menu  // 2、单独设置menu,就不会触发button.action

总结

NSStatusItem作为NSStatusBar的重要组件,由系统的 NSStatusBar 创建的对象。不能直接实例化 NSStatusItem,必须通过 NSStatusBar 来创建。

相关文章

1、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/
2、macOS单个菜单项类NSMenuItem:https://fangjunyu.com/2025/06/25/macos%e5%8d%95%e4%b8%aa%e8%8f%9c%e5%8d%95%e9%a1%b9%e7%b1%bbnsmenuitem/

3、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/

4、macOS状态栏不显示图标的问题:https://fangjunyu.com/2025/06/26/macos%e8%8f%9c%e5%8d%95%e6%a0%8f%e4%b8%8d%e6%98%be%e7%a4%ba%e5%9b%be%e6%a0%87%e7%9a%84%e9%97%ae%e9%a2%98/

   

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

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

发表回复

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