【问题标题】:Customizing the app's Main Menu in SwiftUI environment在 SwiftUI 环境中自定义应用程序的主菜单
【发布时间】:2022-11-14 09:21:31
【问题描述】:

我正在创建一个 NSViewRepresentable 视图以在 SwiftUI 代码中使用 NSView。 我需要NSViewRepresentable 代码通过NSApp.mainMenu 属性自定义应用程序的主菜单。

但事实证明,该任务的合格 AppKit 代码在 SwiftUI 环境中不起作用。通过调试主菜单,我看到我添加到菜单中的项目出现在 NSMenu 实例中,但这些项目没有显示在 SwiftUI 应用程序菜单中,但它们显示在 AppKit 应用程序菜单中。

调试显示,在 SwiftUI 环境下,应用的主菜单使用 SwiftUI.AppKitMainMenuItem 对象而不是 NSMenuItem。但它是我不能使用的苹果私人课程。

如何在 SwiftUI 环境中实现这一点?我真的需要在我的 NSViewRepresentable 类中使用 Cocoa 代码库来完成这项工作,因为我正在为 AppKit、UIKit 和 SwiftUI 开发通用扩展。

【问题讨论】:

    标签: swift macos swiftui


    【解决方案1】:

    SwiftUI macOS 主菜单

    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
    

    【讨论】:

    • 正如我所说,我真的需要使用 AppKit 对象来完成这件事,只是因为我正在开发一个通用的 Swift 包。它没有@main App SwiftUI 结构。
    • 请发布您的代码。
    • 我不知道我应该发布什么代码。我有一种方法可以在应用程序主菜单中创建一堆子菜单。当我的视图被实例化时,我调用该方法。在 AppKit 环境中一切正常,这些子菜单已添加到应用程序主菜单中。但是,当我在 SwiftUI 环境中将视图包装在 NSViewRepresentable 中时,它不会起作用——这些子菜单是在内部添加的(主菜单项数组有它们),但菜单不会显示它们。
    • 我的 Swift 包中没有 App 结构。该包仅提供 NSViewRepresentable 视图,添加该包的应用程序可以在包外使用该视图。所以一切都必须在 NSViewRepresentable 级别上完成。我猜这是 SwiftUI 的一个错误。
    • 我已经向 Apple 提交了错误报告。我相信这是一个 SwiftUI 错误。
    【解决方案2】:

    我也对此进行了研究 - 这可能是 swiftUI 上的一个错误,它不断破坏菜单。

    无论如何 - 看看这个线程: SwiftUI Update the mainMenu [SOLVED] kludgey

    把它放在AppDelegate.applicationWillUpdate函数的DispatchQueue.main.async闭包中

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

    【讨论】:

      猜你喜欢
      • 2020-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-28
      相关资源
      最近更新 更多