我正在创建一个
NSViewRepresentable
视图以在 SwiftUI 代码中使用 NSView
。
我需要 NSViewRepresentable
代码来通过 NSApp.mainMenu
属性自定义应用程序的主菜单。
但事实证明,该任务的合格 AppKit 代码在 SwiftUI 环境中不起作用。通过调试主菜单,我看到添加到菜单的项目出现在 NSMenu 实例中,但这些项目没有显示在 SwiftUI 应用程序菜单中,但它们显示在 AppKit 应用程序菜单中。
调试显示,在 SwiftUI 环境中,应用程序的主菜单使用
SwiftUI.AppKitMainMenuItem
对象而不是 NSMenuItem
。但它是苹果的私人课程我不能使用。
在SwiftUI环境下如何实现?我确实需要在 NSViewRepresentable 类中使用 Cocoa 代码库来完成此操作,因为我正在为 AppKit、UIKit 和 SwiftUI 开发通用扩展。
Xcode 14.0+,macOS 蒙特利 12.5.1
要从主菜单中删除所有五个默认菜单项,请使用以下方法:
import SwiftUI
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let menus = ["File","Edit","View","Window","Help"]
menus.forEach { menu in
NSApp.mainMenu?.item(withTitle: menu).map {
NSApp.mainMenu?.removeItem($0)
}
}
}
}
然后将 @NSApplicationDelegateAdaptor 属性包装器放入 SwiftUI
@main
App 的结构中。要生成自定义菜单项,请使用 .commands {...}
修饰符。
@main struct YourApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
.commands {
CommandMenu("Custom") {
Button("Add Item", action: { } )
.keyboardShortcut("N")
Divider()
Button("Edit Item", action: { } )
.keyboardShortcut("E")
}
}
}
}
或者,您可以将
@NSApplicationDelegateAdaptor
属性包装器放入 ContentView
结构中,而不是 YourApp
@main 结构。
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
我也研究过这个问题 - 这可能是 swiftUI 上的一个错误,一直破坏菜单。
无论如何 - 看看这个线程: SwiftUI 更新主菜单 [已解决] kludgey
将其放入
DispatchQueue.main.async
函数中的 AppDelegate.applicationWillUpdate
闭包中
import Foundation
import AppKit
public class AppDelegate: NSObject, NSApplicationDelegate {
public func applicationWillUpdate(_ notification: Notification) {
DispatchQueue.main.async {
let currentMainMenu = NSApplication.shared.mainMenu
//
// Do your menu stuff here
//
// The below removes the Edit menu
//
let editMenu: NSMenuItem? = currentMainMenu?.item(withTitle: "Edit")
if nil != editMenu {
NSApp.mainMenu?.removeItem(editMenu!)
}
}
}
}
据我了解,我需要在 App 结构中使用 CommandGroup 在 SwiftUI 中设置菜单项。所以我有以下内容。
import SwiftUI
@main
struct Oh_My_App: App {
var body: some Scene {
WindowGroup {
ContentView(menuObservable: menuObservable)
}
.commands {
CommandGroup(replacing: .systemServices){
}
CommandMenu("Help") {
Button("Button 1", action: {
} )
.keyboardShortcut("N")
Divider()
Button("Button 2", action: {
} )
.keyboardShortcut("S")
Divider()
Button("Button 3", action: {
} )
.keyboardShortcut("R")
}
}
}
}
CommandGroup(replacing: .systemServices) 行将默认的“服务”菜单替换为空组,从而有效地删除了默认项目。如果您想覆盖或删除内置菜单命令,这会很有用。
使用 CommandMenu("Help") 将名为“Help”的新自定义菜单部分添加到应用程序的菜单栏中。 在此菜单中,定义了三个按钮: “按钮 1”带有键盘快捷键“N”。 “按钮 2”带有键盘快捷键“S”。 “按钮 3”带有键盘快捷键“R”。 每个按钮都有一个关联的操作块,该块当前为空。这些块可以填充自定义功能。 Divider() 元素用于在视觉上分隔按钮,增强菜单的布局。
如果您想删除所有默认菜单项。这是最好的方法:
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
setupMenuBar()
}
private func setupMenuBar() {
if let mainMenu = NSApplication.shared.mainMenu {
mainMenu.items.enumerated().forEach { index, item in
if index != 0 && index != 4 {
item.isHidden = true
}
}
}
}
}
它将删除所有 5 个默认主菜单项。