macOS 开发之菜单栏形式的状态栏小工具

不知道大家有没有用过菜单栏形式的状态栏工具,类似于之前写的 NSPopover 的工具在系统顶栏占用一个图标,不同的是点击之后弹出的不是弹窗而是一个菜单,就像下面截图展示的工具,本文就讲一下如何实现。

平台

  • macOS 10.15
  • Xcode 11.1
  • Swift 5.1

本文使用上述平台实现验证,版本不同可能有些差异,但基本思路一致。

工程新建及配置

  • 打开xcode新建工程, macOS -> App -> Next:

  • 输入工程名称:MenuToolDemolanguage 选择SwiftUser Interface 选择 SwiftUI(本文不会用到 SwiftUI)点击 Next:

  • 选择合适的目录,点击 create 即可打开创建的新工程;

  • 点击运行按钮,可以看到程序运行,出现一个显示 hello world 的窗口,同时dock上出现了应用图标,这不是我们想要的,设置一下不显示它们:

    • 工程导航栏选中工程MenuToolDemo,打开Info标签页;

    • 可以看到Custom macOS application Target Properties组,添加新的配置Application is agent(UI Element),布尔属性,值为 YES:

  • 重新运行程序,可以看到已经不显示Dock图标;

  • 删除 ContentView.swift 文件;

  • 打开文件AppDelegate.swift,删掉两个地方代码:

    var window: NSWindow!
    
    // Create the SwiftUI view that provides the window contents.
    let contentView = ContentView()
    
    // Create the window and set the content view.
    window = NSWindow(
        contentRect: NSRect(x: 0, y: 0, width: 480, height: 300),
        styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
        backing: .buffered, defer: false)
    window.center()
    window.setFrameAutosaveName("Main Window")
    window.contentView = NSHostingView(rootView: contentView)
    window.makeKeyAndOrderFront(nil)
    
  • 再次运行程序,主窗口也不显示了,连菜单栏也木有了,不要着急,咱继续。

添加状态栏按钮

打开文件AppDelegate.swift,在类中添加属性,这一步是创建一个状态栏按钮,设置宽度属性NSStatusItem.squareLength,代码如下:

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

状态栏按钮总该需要一个图标吧!打开Assets.xcassets,右击显示AppIcon下方的空白区,选择New Image Set,重命名为statusIcon,当然这个名字随便定,选中这个图集,会看到右侧有配置区,配置图集按照Template Image渲染。

看到有三个虚线框空白区,这就是图片区,状态栏按钮的图片基本大小为 $18px\times18px$ ,还需2倍和3倍的适用于视网膜屏幕的mac,像素分别是 $36px\times36px$ 和 $54px\times54px$ ,可以使用以下我提供的图标:

分别将图拖到对应位置:

找到applicationDidFinishLaunching 在其中添加以下代码,为状态栏按钮配置图标和行为:

if let button = statusItem.button {
    button.image = NSImage(named: "StatusIcon")
}

此时运行程序会看到状态栏中出现了我们定义的按钮,当然此时鼠标单击没有任何动作发生。

前面设置图片集渲染方式为Template Image,是为了适配不同的状态栏主题,因为macOS还有个暗黑主题不是?

两种主题下的效果如下:

添加菜单

打开 Main.storyboard 文件,使用以下两种方式中的任意一种,打开控件库窗口:

  • 按下快捷键 Command + Shift + L

  • 点击 Xcode 窗口右上角的 ➕ 按钮

在搜索框中输入 Menu,就会检索到 NSMenu 控件:

鼠标左键在控件单击不松拖放到文件 Main.storyboard 的左边栏 First Responder 下面:

按下快捷键 Ctrl + Option + Command + Enter(⌃⌥⌘⏎) 打开 Assitant 编辑器:

按住 Ctrl 键,鼠标左键按住 Menu 控件不松拖动至辅助编辑器的文件 AppDelegate.swift 中,在弹出的属性添加弹窗中输入属性名 menu,点击 Connect 就会看到 AppDelegate.swift 中出现 menu 属性: