如何在 macos Big Sur 中使用 SwiftUI 创建状态栏图标和菜单

Ruz*_*ard 21 macos swiftui macos-big-sur

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

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

电池菜单截图 查看电池菜单的层次结构

编辑:

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

ARG*_*Geo 12

MenuBarExtra(macOS 文图拉)

在 macOS 13.0+ 和 Xcode 14.0+ 中,MenuBarExtra结构允许您创建系统菜单栏,类似于 NSStatusBar 的图标和菜单。MenuBarExtra当您想要提供对常用功能的访问时,即使您的应用程序未处于活动状态,也可以使用 a 。

在此输入图像描述

import SwiftUI

@available(macOS 13.0, *)                       // macOS Ventura
@main struct StatusBarApp: App {
    
    @State private var command: String = "a"
       
    var body: some Scene {

        MenuBarExtra(command, systemImage: "\(command).circle") {
           
            Button("Uno") { command = "a" }
                .keyboardShortcut("U")
           
            Button("Dos") { command = "b" }
                .keyboardShortcut("D")
           
            Divider()

            Button("Salir") { NSApplication.shared.terminate(nil) }
                .keyboardShortcut("S")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

删除 Dock 中的应用程序图标

在 Xcode 的Info选项卡中,选择Application is agent (UIElement)并将其值设置为YES

在此输入图像描述

或者您可以通过编程方式隐藏应用程序的图标。

  • 感谢更新。苹果花了一些时间才获得状态栏 API。我想添加到您的回复中 - 可以将任何 SwiftUI 视图添加到“MenuBarExtra”,而不仅仅是按钮。 (2认同)
  • 当我使用自定义 SVG 图标时,它会遇到缩放问题 - 它很丑而且像素化:( (2认同)

Ruz*_*ard 11

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

找到了一种在显示这一点的方法,而没有烦人的NSPopover. 您需要拥有AppDelegateNSApplicationDelegateAdaptor以防万一您使用 SwiftUI App 生命周期。然后你创建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"
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


dav*_*dev 9

在 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"
}
Run Code Online (Sandbox Code Playgroud)

这将向您的菜单栏添加一个带有自定义菜单的按钮。

在此输入图像描述

编辑 - 如何使用 SwiftUI View

正如您所问,这是如何使用 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
Run Code Online (Sandbox Code Playgroud)

然后,不显示 NSMenu,而是切换弹出框:

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

通过以下操作:

@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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

  • 不幸的是,您发布的代码是创建这些菜单的 AppKit 方式。这不是 SwiftUI 方式,也不支持 SwiftIUI 视图。 (6认同)
  • 是的,但这不是我问的。我有兴趣在顶部菜单中显示 swiftUI。 (3认同)
  • 我添加了一个没有 NSPopover 的替代解决方案。也许您也发现它很有用。 (3认同)