macOS Menu Bar + NSWindowController

最近想开发一个基于 Menu Bar 的项目,然后把 NSWindowController 作为内容展示和交互。看到很多的教程都是在 Menu Bar 上添加 NSPopover,然后配合 NSViewController 工作,这个方案会在 Menu Bar 下显示 NSPopover 的箭头,不符合我的需求,我想要的效果是 macOS 系统调节音量、选择输入法这种类似的窗口。

截屏2023-06-08 18.43.56.png

一、新建一个 Menu Bar 的 macOS 项目

打开 Main.storyboard,删除 WindowController 和 ViewController

截屏2023-06-08 18.59.37.png

在 info 中增加一个配置 Application is agent (UIElement),设置为 YES

截屏2023-06-08 19.00.34.png

二、创建 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

截屏2023-06-08 19.39.45.png

截屏2023-06-08 19.16.16.png

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
    }
}

截屏2023-06-08 19.24.46.png

五、监控鼠标点击其他 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 下载

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYhyzddb' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片