如何像在 macOS Big Sur 中一样使用 SwiftUI 创建状态栏图标和菜单

How to create status bar icon & menu with SwiftUI like in macos Big Sur

什么是用于创建状态栏菜单的 SwiftUI API?

根据辅助功能检查器,Apple 似乎在电池和 WiFi 菜单中使用了 SwiftUI 视图。附加电池菜单的屏幕截图,以及它的视图层次结构。

编辑:

将解决方案作为单独的答案发布。

在 AppDelegate 中添加以下代码:

// Create the status item in the Menu bar 
self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))

// Add a menu and a menu item
let menu = NSMenu()
let editMenuItem = NSMenuItem()
editMenuItem.title = "Edit"
menu.addItem(editMenuItem)

//Set the menu 
self.statusBarItem.menu = menu

//This is the button which appears in the Status bar
if let button = self.statusBarItem.button {
    button.title = "Here"
}

这会将带有自定义菜单的按钮添加到您的菜单栏。

编辑 - 如何使用 SwiftUI 视图

如您所问,这里是如何使用 SwiftUI 视图的答案。

首先创建一个 NSPopover,然后将您的 SwiftUI 视图包装在 NSHostingController.

var popover: NSPopover


let popover = NSPopover()
popover.contentSize = NSSize(width: 350, height: 350)
popover.behavior = .transient
popover.contentViewController = NSHostingController(rootView: contentView)
    self.popover = popover

然后不显示 NSMenu,切换弹出窗口:

self.statusBarItem = NSStatusBar.system.statusItem(withLength: CGFloat(NSStatusItem.variableLength))
if let button = self.statusBarItem.button {
     button.title = "Click"
     button.action = #selector(showPopover(_:))
}

执行以下操作:

@objc func showPopover(_ sender: AnyObject?) {
    if let button = self.statusBarItem.button
    {
        if self.popover.isShown {
            self.popover.performClose(sender)
        } else {
            self.popover.show(relativeTo: button.bounds, of: button, preferredEdge: NSRectEdge.minY)
        }
    }
}

由于最近这个问题受到了更多的关注,而唯一的回复并没有完全解决问题我想重复我的问题的编辑部分并将其标记为已解决。

找到了一种在 中显示它的方法,而不用烦人 NSPopover。如果您使用 SwiftUI 应用程序生命周期,则需要 AppDelegateNSApplicationDelegateAdaptor。然后你创建 NSMenu 并添加 NSMenuItem 可以有一个自定义视图。

代码如下:

let contentView = VStack {
    Text("Test Text")
    Spacer()
    HStack {
        Text("Test Text")
        Text("Test Text")
    }
    Spacer()
    Text("Test Text")
}

let view = NSHostingView(rootView: contentView)

// Don't forget to set the frame, otherwise it won't be shown.
view.frame = NSRect(x: 0, y: 0, width: 200, height: 200)
        
let menuItem = NSMenuItem()
menuItem.view = view
        
let menu = NSMenu()
menu.addItem(menuItem)
        
// StatusItem is stored as a class property.
self.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
self.statusItem?.menu = menu
self.statusItem?.button?.title = "Test"