swift - 仅在 NSStatusBarButton 右键单击​​时显示 NSMenu?

标签 swift macos cocoa swiftui appkit

我有以下代码:(可以复制粘贴到新的 macOS 项目)

import Cocoa
import SwiftUI

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    var statusBarItem: NSStatusItem!

    func applicationDidFinishLaunching(_ aNotification: Notification) {

        let statusBar = NSStatusBar.system
        statusBarItem = statusBar.statusItem(
            withLength: NSStatusItem.squareLength)
        statusBarItem.button?.title = "🍎"

        // Setting action
        statusBarItem.button?.action = #selector(self.statusBarButtonClicked(sender:))
        statusBarItem.button?.sendAction(on: [.leftMouseUp])

        let statusBarMenu = NSMenu(title: "Status Bar Menu")
        statusBarMenu.addItem(
            withTitle: "Order an apple",
            action: #selector(AppDelegate.orderAnApple),
            keyEquivalent: "")

        statusBarMenu.addItem(
            withTitle: "Cancel apple order",
            action: #selector(AppDelegate.cancelAppleOrder),
            keyEquivalent: "")

        // Setting menu
        statusBarItem.menu = statusBarMenu
    }

    @objc func statusBarButtonClicked(sender: NSStatusBarButton) {
        let event = NSApp.currentEvent!

        if event.type ==  NSEvent.EventType.rightMouseUp {
            print("Right click!")
        } else {
            print("Left click!")
        }
    }

    @objc func orderAnApple() {
        print("Ordering a apple!")
    }


    @objc func cancelAppleOrder() {
        print("Canceling your order :(")
    }

}

实际行为:左键单击和右键单击都会打开菜单,不会触发 statusBarButtonClicked。
删除此行后:

statusBarItem.menu = statusBarMenu

statusBarButtonClicked 左键单击时触发,菜单未显示(如预期)

期望的行为:右键单击菜单打开,左键单击菜单不打开,操作被触发。
我如何实现它?

编辑

在 @red_menace 评论的帮助下,我成功实现了预期的行为:

import Cocoa
import SwiftUI

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    var statusBarItem: NSStatusItem!
    var menu: NSMenu!

    func applicationDidFinishLaunching(_ aNotification: Notification) {

        let statusBar = NSStatusBar.system
        statusBarItem = statusBar.statusItem(
            withLength: NSStatusItem.squareLength)
        statusBarItem.button?.title = "🍎"

        // Setting action
        statusBarItem.button?.action = #selector(self.statusBarButtonClicked(sender:))
        statusBarItem.button?.sendAction(on: [.leftMouseUp, .rightMouseUp])

        let statusBarMenu = NSMenu(title: "Status Bar Menu")
        statusBarMenu.addItem(
            withTitle: "Order an apple",
            action: #selector(AppDelegate.orderAnApple),
            keyEquivalent: "")

        statusBarMenu.addItem(
            withTitle: "Cancel apple order",
            action: #selector(AppDelegate.cancelAppleOrder),
            keyEquivalent: "")

        // Setting menu
        menu = statusBarMenu
    }

    @objc func statusBarButtonClicked(sender: NSStatusBarButton) {
        let event = NSApp.currentEvent!

        if event.type ==  NSEvent.EventType.rightMouseUp {
            statusBarItem.popUpMenu(menu)
        } else {
            print("Left click!")
        }
    }

    @objc func orderAnApple() {
        print("Ordering a apple!")
    }


    @objc func cancelAppleOrder() {
        print("Canceling your order :(")
    }

}

但是 Xcode 说 openMenu func 在 10.14 中已弃用,并告诉我改用菜单属性。有没有办法使用新的 API 实现所需的行为?

最佳答案

显示菜单的常用方法是将菜单分配给状态项,当单击状态项按钮时将显示菜单。由于 popUpMenu 已被弃用,因此需要另一种方式来在不同条件下显示菜单。如果您希望右键单击使用实际的状态项菜单,而不是仅在状态项位置显示上下文菜单,则可以将状态项菜单属性保留为 nil,直到您想要显示它为止。

我已经调整了您的代码,以将 statusBarItemstatusBarMenu 引用分开,仅将菜单添加到单击操作方法中的状态项。在操作方法中,添加菜单后,在状态按钮上执行正常单击即可删除菜单。由于单击按钮时状态项将始终显示其菜单,因此添加了 NSMenuDelegate 方法,以在菜单关闭时将菜单属性设置为 nil,从而恢复原操作:

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
    // keep status item and menu separate
    var statusBarItem: NSStatusItem!
    var statusBarMenu: NSMenu!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        let statusBar = NSStatusBar.system
        statusBarItem = statusBar.statusItem(withLength: NSStatusItem.squareLength)
        statusBarItem.button?.title = "🍎"

        statusBarItem.button?.action = #selector(self.statusBarButtonClicked(sender:))
        statusBarItem.button?.sendAction(on: [.leftMouseUp, .rightMouseUp])

        statusBarMenu = NSMenu(title: "Status Bar Menu")
        statusBarMenu.delegate = self
        statusBarMenu.addItem(
            withTitle: "Order an apple",
            action: #selector(AppDelegate.orderAnApple),
            keyEquivalent: "")
        statusBarMenu.addItem(
            withTitle: "Cancel apple order",
            action: #selector(AppDelegate.cancelAppleOrder),
            keyEquivalent: "")
    }

    @objc func statusBarButtonClicked(sender: NSStatusBarButton) {
        let event = NSApp.currentEvent!
        if event.type ==  NSEvent.EventType.rightMouseUp {
            print("Right click!")
            statusBarItem.menu = statusBarMenu // add menu to button...
            statusBarItem.button?.performClick(nil) // ...and click
        } else {
            print("Left click!")
        }
    }

    @objc func menuDidClose(_ menu: NSMenu) {
        statusBarItem.menu = nil // remove menu so button works as before
    }

    @objc func orderAnApple() {
        print("Ordering a apple!")
    }

    @objc func cancelAppleOrder() {
        print("Canceling your order :(")
    }

}

关于swift - 仅在 NSStatusBarButton 右键单击​​时显示 NSMenu?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59635971/

相关文章:

ios - ekeventviewcontroller隐藏删除按钮

macos - Dylibs 和 OS X

objective-c - 如何链接到 C 代码块中的委托(delegate)

objective-c - 如何在简单的 Core Data 应用程序中命名撤消和重做操作?

ios - 无法快速获取 View Controller 的索引,该 View Controller 是 UINavigationController 的 View Controller

ios - CLLocationManager 和 CLLocationManagerDelegate 的问题

swift - 初始 View Controller 应该调用 "deinit"吗?

macos - 如何检查 Mac 操作系统上是否安装了 Grails?

xcode - Xcode (mac) 上的图形故障

objective-c - 在 Objective C 中动态调用类方法