不知道大家有没有用过菜单栏形式的状态栏工具,类似于之前写的 NSPopover 的工具在系统顶栏占用一个图标,不同的是点击之后弹出的不是弹窗而是一个菜单,就像下面截图展示的工具,本文就讲一下如何实现。
平台
- macOS 10.15
- Xcode 11.1
- Swift 5.1
本文使用上述平台实现验证,版本不同可能有些差异,但基本思路一致。
工程新建及配置
- 打开xcode新建工程, macOS -> App -> Next:
- 输入工程名称:
MenuToolDemo
,language 选择Swift
,User Interface 选择 SwiftUI(本文不会用到 SwiftUI)点击 Next:
- 选择合适的目录,点击 create
- 点击运行按钮,可以看到程序运行,出现一个显示 hello world
- 工程导航栏选中工程
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
渲染。
看到有三个虚线框空白区,这就是图片区,状态栏按钮的图片基本大小为
,还需2倍和3倍的适用于视网膜屏幕的mac,像素分别是
和
,可以使用以下我提供的图标:
分别将图拖到对应位置:
找到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
https://www.zhihu.com/video/1170069061483552768
打开文件 AppDelegate.swift
,在上面配置按钮图标的代码上面添加以下代码:
statusItem.menu = menu
运行程序,鼠标左键和右键单击菜单栏按钮都会弹出我们添加的按钮,包含 item1、item2 和 item3。
配置菜单
下面我们看一下菜单即菜单项的基本使用。
菜单项基本属性
打开文件 main.storyboard,单击 menu,选中 Item1,可以在右侧属性检查器(Attributes Inspector) 中看到各个属性。选中 Item2 和 Item3,按 Delete
键删除,选中 Item1
名称 - Title
修改 Item1 的属性检查器的 Title ,比如修改成 退出,就会修改此菜单项现实的名称。
状态 - State
State 表示选中状态(check),三种状态:on、off、mixed。当设置为 on 的时候会看到菜单项前出现一个对号,这里设置为 off
图标 - Image
可以设置通用的图标 Image,同时也可以设置各种状态的图标。这里我们只设置 Image 为 NSStopProgressFreestandingTemplate,编译运行:
行为 - Action
你会好奇为什么上面运行 退出 是灰色的,那是因为我们还没为它指定行为,类似于前面绑定 menu 属性的操作,同样的操作,只不过这次是按住 Ctrl
键的同时,鼠标左键单击 退出 菜单项不松拖动到辅助编辑器的 AppDelegate.swift
文件中,绑定一个名叫 quitApp
的 action。
实现 action 的功能为退出应用,最终 quitApp
方法如下:
@IBAction func quitApp(_ sender: Any) {
NSApplication.shared.terminate(self)
}
编译运行程序,单击菜单栏的按钮可以看到 退出
快捷键
每个菜单项执行行为可以绑定快捷键的,就是设置在属性检查起中的 Key Equivalent 属性,我们这里设置为 Q,运行程序,可以看到菜单项名称后面多了一个 Q,当显示菜单时按下 Q 键,等同于点击了 退出。
多级菜单
多级菜单也很容易实现。
- 添加新的菜单项
打开文件main.storyboard
,按下快捷键Command
+Shift
+L
,在控件库弹窗输入 Menu item,拖动控件 Menu Item 到 退出 - 威刚添加的菜单项添加菜单
打开控件库弹窗,拖动 Menu
运行程序,点击菜单栏按钮,鼠标放到菜单项 Item
总结
本文简单讲了菜单形式菜单栏工具的基本实现,推一反三便可以实现更丰富的小工具了。最终实现的工程文件已经打包在下面:
MenuToolDemopichome-1254392422.cos.ap-chengdu.myqcloud.com
博客原文:
macOS 开发之菜单栏形式的状态栏小工具