NSStatusBar 是 macOS 中用于创建菜单栏图标(系统右上角)的 API,很多 App(比如 Dropbox、网易云音乐、Clipy)都使用它来常驻状态栏,并提供快捷操作。
可以用 NSStatusBar 创建一个常驻菜单图标,并附加菜单或行为。

状态栏菜单
import AppKit
import SwiftUI
class StatusBarController {
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 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)
let hideItem = NSMenuItem(title: "隐藏状态栏", action: #selector(removeStatusItem), keyEquivalent: "h")
hideItem.target = self
menu.addItem(hideItem)
statusItem.menu = menu
}
@objc func openApp() {
print("打开 App")
NSApp.activate(ignoringOtherApps: true)
}
@objc func removeStatusItem() {
if let item = statusItem {
NSStatusBar.system.removeStatusItem(item)
statusItem = nil
}
}
}
配合 SwiftUI 使用方法
可以在 AppDelegate 中初始化:
import AppKit
import SwiftUI
class AppDelegate: NSObject, NSApplicationDelegate {
var statusBarController: StatusBarController?
func applicationDidFinishLaunching(_ notification: Notification) {
statusBarController = StatusBarController()
}
}
在入口文件中使用:
@main
struct MyApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}

状态栏详解
StatusBarController状态控制器
创建一个 StatusBarController 类,定义状态栏图标对象statusItem,类型为NSStatusItem:
class StatusBarController {
private var statusItem: NSStatusItem!
在初始化中,创建状态栏图标和菜单。
init() {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
使用 .variableLength 自动根据内容大小设定宽度(图标 + 文字)。
if let button = statusItem.button {
button.image = NSImage(named: "templateIcon")
}
设置图标图片,在后面的“设置状态栏图标”部分,会讲到具体的设置图标流程。
使用NSMenu创建菜单对象:
let menu = NSMenu()
添加菜单项:
let openItem = NSMenuItem(title: "打开 App", action: #selector(openApp), keyEquivalent: "o")
“打开 App”:调用 openApp(),并设为快捷键 ⌘O。
let quitItem = NSMenuItem(title: "退出", action: #selector(NSApp.terminate(_:)), keyEquivalent: "q")
“退出”:直接调用系统的 terminate 方法(快捷键 ⌘Q)。
let hideItem = NSMenuItem(title: "隐藏状态栏", action: #selector(removeStatusItem), keyEquivalent: "h")
“隐藏状态栏”:移除状态栏图标。
其中,”打开App”和“隐藏状态栏” 设置了 target = self,而“退出”没有设置target:
let openItem = NSMenuItem(title: "打开 App", action: #selector(openApp), keyEquivalent: "o")
openItem.target = self
menu.addItem(openItem)
let quitItem = NSMenuItem(title: "退出", action: #selector(NSApp.terminate(_:)), keyEquivalent: "q")
menu.addItem(quitItem)
这是因为,“打开App” 和“隐藏状态栏”的action是我自定义的方法,系统不知道找谁调用,所以必须设置target = self。
而“退出”则是直接调用NSApp.terminate(_:)方法,NSApp本身就是系统的全局对象,已经是target。
行为函数:
@objc func openApp() {
print("打开 App")
NSApp.activate(ignoringOtherApps: true)
}
@objc func removeStatusItem() {
if let item = statusItem {
NSStatusBar.system.removeStatusItem(item)
statusItem = nil
}
}
这两个@objc方法,分别为激活主应用窗口和在系统中移除图标。
不了解@objc方法,可以进一步阅读《Swift @objc属性声明》和《Swift #selector语法》。
AppDelegate代理类
class AppDelegate: NSObject, NSApplicationDelegate {
var statusBarController: StatusBarController?
func applicationDidFinishLaunching(_ notification: Notification) {
statusBarController = StatusBarController()
}
}
AppDelegate 实现了 NSApplicationDelegate。
在应用启动完成后初始化 StatusBarController,显示菜单栏图标。
SwiftUI 应用入口
@main
struct MyApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
使用 @NSApplicationDelegateAdaptor 将 AppDelegate 注入 SwiftUI 生命周期。
AppDelegate 会自动接收系统生命周期回调。
常用功能
1、添加状态栏图标:NSStatusBar.system.statusItem,用于创建状态栏项目。
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
从系统状态栏获取一个图标槽位(NSStatusItem)。
.variableLength 表示宽度可变(根据图标或文字宽度自动变化)。
2、设置图标/文本:statusItem.button?.image / title,显示图标或文本。
if let button = statusItem.button {
button.image = NSImage(named: "templateIcon")
button.action = nil
button.target = self
}
通过button.image可以实现状态栏图标。
3、设置菜单:statusItem.menu,绑定 NSMenu。
4、响应点击:button.action / target,处理点击行为。
if let button = statusItem.button {
button.image = NSImage(named: "templateIcon")
button.action = #selector(statusBarButtonClicked)
button.target = self
}
通常情况下,设置button时,还需要设置button.action / target,这表示点击图标执行自定义的方法。
但是如果设置了menu:
statusItem.menu = menu
那么,点击图标自动弹出菜单,这里就是系统托管模式,不会触发按钮事件。
因此,就可以忽略button.action / target:
if let button = statusItem.button {
button.image = NSImage(named: "templateIcon")
}
因为,在设置menu后,即使配置 button.action / target,macOS也会忽略按钮的 action 和 target。
5、移除图标:NSStatusBar.system.removeStatusItem(_:),删除状态栏图标。
NSStatusBar.system.removeStatusItem(item)
移除图标后,如果想要恢复状态栏。可以将创建状态栏的代码封装到方法中,重新调用。
设置状态栏图标
因此,在创建状态栏图标时,可以在原有的mac图标基础上进行修改:将图标的背景改为空白区域,状态栏图标没有边距。

修改后,状态栏的图标是包含透明度的PNG图片,状态栏图标目前推荐的格式为PNG或PDF(矢量)。
macOS 状态栏图标推荐尺寸是 18×18 pt(对应 36×36 px @2x)。如果图片太大/太小/没有内边距,可能不会显示或显示异常。

设置后,显示的状态栏图标:

状态栏图标建议使用模板图(template image),模板图能适配深色/浅色模式。
在Assets文件夹中,找到状态栏图标,在右侧选项中找到“Render As”,设置为“Template Image”。

设置后,图标变成模版图,完成状态栏图标的设置。

总结
NSStatusBar 是系统右上角常驻图标的入口,配合 NSMenu 可构建强大的快捷控制。可与 SwiftUI 配合,通过 AppDelegate 初始化,是很多“轻量工具类应用”的标配入口。
如果在状态栏中没有找到图标,可能是图标过大,或者状态栏图标过多,导致应用图标未显示。
如果想要状态栏弹出SwiftUI视图,可以通过NSPopover实现:
let popover = NSPopover()
popover.contentViewController = NSHostingController(rootView: MySwiftUIView())
popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
参考文章
1、NSStatusBar:https://developer.apple.com/documentation/appkit/nsstatusbar
2、Swift @objc属性声明:https://fangjunyu.com/2025/04/06/swift-objc%e5%b1%9e%e6%80%a7%e5%a3%b0%e6%98%8e/
3、Swift #selector语法:https://fangjunyu.com/2025/06/23/swift-selector%e8%af%ad%e6%b3%95/