最近想开发一个基于 Menu Bar 的项目,然后把 NSWindowController 作为内容展示和交互。看到很多的教程都是在 Menu Bar 上添加 NSPopover,然后配合 NSViewController 工作,这个方案会在 Menu Bar 下显示 NSPopover 的箭头,不符合我的需求,我想要的效果是 macOS 系统调节音量、选择输入法这种类似的窗口。
一、新建一个 Menu Bar 的 macOS 项目
打开 Main.storyboard,删除 WindowController 和 ViewController
在 info 中增加一个配置 Application is agent (UIElement),设置为 YES
二、创建 NSStatusItem
拖入 Menu Bar 的 icon,新建 StatusBarMenu 文件,代码如下
import AppKit
class StatusBarMenu: NSObject {
var statusBarItem: NSStatusItem!
override init() {
super.init()
initStatusBarItem()
}
// 初始化状态栏Button
func initStatusBarItem() {
self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.squareLength))
if let button = self.statusBarItem.button {
button.image = NSImage(named: "menubar")
button.image?.size = NSSize(width: 18.0, height: 18.0)
button.image?.isTemplate = true
button.action = #selector(AppDelegate.instance.togglePop)
button.sendAction(on: [.leftMouseUp, .rightMouseUp])
}
}
}
三、AppDelegate 中去使用 NSStatusItem
// 申明属性
static private(set) var instance: AppDelegate! = nil
var statusMenu: StatusBarMenu?
var showPopWindow = false // 记录是否显示主窗口
func applicationDidFinishLaunching(_ aNotification: Notification) {
AppDelegate.instance = self
self.statusMenu = StatusBarMenu()
}
// MARK: 显示和隐藏窗口
@objc func togglePop() {
}
现在运行项目,可以在顶部菜单栏看到 Menu Bar 的 icon 了。
四、创建主窗口 PopWindowController
在 PopWindowController.xib 中拖入 Visual Effect View
import Cocoa
class PopWindowController: NSWindowController {
override var windowNibName: NSNib.Name {
return NSNib.Name("PopWindowController")
}
override func windowDidLoad() {
super.windowDidLoad()
}
}
回到 AppDelegate 中使用 PopWindowController
// 申明属性
lazy var popWindowController = PopWindowController()
@objc func togglePop() {
if !showPopWindow {
// 计算窗口的位置
if let event = NSApp.currentEvent, let window = event.window, let popWindow = popWindowController.window {
let eventFrame = window.frame
let eventOrigin = eventFrame.origin
let popWindowTopLeftPosition = CGPoint(x: eventOrigin.x, y: eventOrigin.y)
popWindow.setFrameTopLeftPoint(popWindowTopLeftPosition)
popWindow.makeKeyAndOrderFront(self)
NSApp.activate(ignoringOtherApps: true)
showPopWindow = true
}
} else {
if let popWindow = popWindowController.window {
popWindow.orderOut(nil)
showPopWindow = false
}
}
}
这个时候运行,可以看到窗口已经出现,但是窗口是可拖动的,这明显不合适的。
解决方法是在 PopWindowController 中设置窗口不可移动。
import Cocoa
class PopWindowController: NSWindowController {
override var windowNibName: NSNib.Name {
return NSNib.Name("PopWindowController")
}
override func windowDidLoad() {
super.windowDidLoad()
// 窗口不可移动
window?.isMovable = false
}
}
五、监控鼠标点击其他 Menu Bar
到目前为止,整体效果已经实现。点击菜单栏其他的图标时,我们的窗口没有消失,这个问题需要解决。
新增两个通知,当点击其他菜单时,调用窗口的隐藏方法即可。
func applicationDidFinishLaunching(_ aNotification: Notification) {
AppDelegate.instance = self
self.statusMenu = StatusBarMenu()
NotificationCenter.default.addObserver(forName: NSWindow.didResignKeyNotification, object: nil, queue: OperationQueue.main) { _ in
self.togglePop()
}
NotificationCenter.default.addObserver(forName: NSWindow.didResignMainNotification, object: nil, queue: OperationQueue.main) { _ in
self.togglePop()
}
}
完整 demo 下载
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END