构建macOS托盘应用的最佳实践——Tray

最近开发了JustTodoDeskNote两个macOS应用,都是启动入口在菜单栏的,通过菜单栏上图标点击,快速执行相关操作,这让我想起我开发第一款菜单栏app Translator时的痛苦。因为想使用最新的SwiftUI作为UI框架,但是此框架构建菜单栏app的资料很少,大多数都是生成一个简单菜单,而不是可以自定义的复杂界面,尤其是用swiftUI构建的界面。 为了解决这一痛点,在总结了JustTodoDeskNote两个macOS应用开发经验后,决心自己做了一个菜单栏应用快捷库Tray,方便以后使用。

一、引入

在macOS项目中,点击File -> Add Package Dependencies ... ,在包管理窗口的搜索框中,复制粘贴https://github.com/boybeak/Tray.git,待检索到库信息,点击Add Package按钮。

二、使用

以SwiftUI应用为例,在代码入口处,声明一个AppDelegate.

2.1 初始化

swift 复制代码
@main
struct DeskNoteApp: App {

    @NSApplicationDelegateAdaptor(AppDelegate.self) var app: AppDelegate

    var body: some Scene {
        Settings {}
    }
}

这里body中的Settings {}代码,是为了隐藏启动时的主窗口。

然后创建AppDelegate类。

swift 复制代码
class AppDelegate: NSObject, NSApplicationDelegate {

    private var tray: Tray!
    
    func applicationDidFinishLaunching(_ notification: Notification) {

        tray = Tray.install(named: "TrayIcon") { tray in 
            self.configTray(tray: tray)
        }
    }
}

引入相关的类Tray并声明我们的托盘管理对象var tray: Tray,并在applicationDidFinishLaunching中为改对象赋值,传入资源文件名称,或者使用SF Symbols也可以,只是参数名要改为systemSymbolName,如果有更多要求,也可以直接以icon为参数名,传入一个NSImage对象。 然后在闭包中配置tray.

2.2 配置托盘信息

swift 复制代码
func configTray(tray: Tray) {
    // 配置左键弹出view
    tray.setView(content: ContentView())
}

这里设置的是一个SwiftUI的View,你也可以设置NSView或者NSViewController,除了界面参数,还有其他三个可选参数:

  1. behavior: NSPopover的behavior,默认值为.transient,即点击窗口以外区域隐藏弹出界面;
  2. level: NSPopover的窗口层级,默认为.floating;
  3. size: NSPopover的大小,默认为nil,即使用View自己配置的大小;

在JustTodo应用中,其效果如下图:

到这里,主要的配置就完成了,如果你不想弹出一个NSPopover,你也可以接管托盘图标的左键事件。

swift 复制代码
tray.setOnLeftClick {
    return true
}

返回true,表示事件完全处理,会阻止默认行为。默认行为就是弹出NSPopover,前提是设置了view。比如在DeskNote中,我接管了此事件,改为弹出笔记的编辑页面。

当然,同样你也可以为右键增加事件。

swift 复制代码
tray.setOnRightClick {
    return true
}

返回true,表示事件完成处理,阻止默认行为。默认行为是弹出菜单,前提是设置了菜单,正如下边代码。

swift 复制代码
let menu = NSMenu()
        
let newNoteMenuItem = NSMenuItem(title: NSLocalizedString("Menu_item_new_note", comment: ""), action: #selector(onNewNoteAction), keyEquivalent: "")
let quitMenuItem = NSMenuItem(title: NSLocalizedString("Menu_item_quit", comment: ""), action: #selector(onQuitAction), keyEquivalent: "")

menu.addItem(newNoteMenuItem)
menu.addItem(quitMenuItem)

tray.setMenu(menu: menu)

效果如下:

这就是一些基本的使用和配置步骤。接下来是一些相关的小建议。

三、建议

3.1 托盘图标尺寸

1x: 1818 2x: 3636 3x: 54*54

3.2 隐藏Docker栏中应用的图标

在Info.plist中,增加一个配置项: Application is agent(UIElement) - YES.

原文来自于我的个人博客Tray - macOS菜单栏app开发库

相关推荐
AD钙奶-lalala7 小时前
在 macOS 上搭建 Flutter 开发环境
flutter·macos
~二向箔~12 小时前
Mac,苹果电脑移动硬盘不显示
macos
vastgrassland13 小时前
对WWDC 2025 Keynote 内容的预测
macos·ios·wwdc
fukai772214 小时前
WWDC 2025 macOS 26有哪些更新点
macos·ios·wwdc
ReadThroughLife18 小时前
【已解决】MACOS M4 芯片使用 Docker Desktop 工具安装 MICROSOFT SQL SERVER
microsoft·macos·docker·容器
獨枭1 天前
配置 macOS 上的 Ruby 开发环境
开发语言·macos·ruby
库奇噜啦呼1 天前
push [特殊字符] present
macos·ios·cocoa
安和昂1 天前
【iOS】多线程NSOperation,NSOperationQueue
macos·ios·cocoa
咕噜签名分发冰淇淋1 天前
Flutter 打包 iOS 苹果 IPA 应用有哪些优势?如何实现?
macos·objective-c·cocoa
st紫月2 天前
用虚拟机安装macos系统之后进入Boot Manager页面
macos