SwiftUI:如何在macOS上更改默认的命令菜单

12
我正在尝试更改macOS SwiftUI应用程序中的默认命令。我想将一些自定义命令附加到“视图”菜单,但是我无法使它正常工作。
这是我尝试过的内容:
@main
struct MyApp: App {

    var body: some Scene {
        
        WindowGroup {
            AppView()
                .environmentObject(AppModel.shared)
                .environment(\.managedObjectContext, AppModel.shared.cloudKitCoordinator.coreDataStack.viewContext)
        }
        .commands {
            ViewCommands()
            SidebarCommands()
            ElementCommands()
            NavigationCommands()
        }
        
    }
    
}

struct ViewCommands: Commands {

    var body: some Commands {
        
        CommandMenu("View") {
            Button("Do something View related") {}
        }
        
    }
    
}

但是它并没有将该命令附加到“View”菜单中,而是创建了一个相同名称的第二个菜单:

enter image description here

有人成功更改默认的命令菜单吗?还是这只是SwiftUI中仍然有些不成熟的部分?

4个回答

8
使用 CommandGroup,该组件具有初始化选项,可以附加或替换现有菜单:
.commands {
    CommandGroup(before: CommandGroupPlacement.newItem) {
        Button("before item") {
            print("before item")
        }
    }

    CommandGroup(replacing: CommandGroupPlacement.appInfo) {
        Button("Custom app info") {
            // show custom app info
        }
    }

    CommandGroup(after: CommandGroupPlacement.newItem) {
        Button("after item") {
            print("after item")
        }
    }
}

不错的教程:https://swiftwithmajid.com/2020/11/24/commands-in-swiftui/


6
接受的答案通常是正确的,建议使用 CommandGroup 而不是 CommandMenu,并且在指向Swift with Majid(一个很棒的网站)方面非常有帮助,但令人恼火的是它没有给出我们想要的确切答案 - View 菜单用哪个 CommandGroupPlacement?为了避免让其他人进行试错,这就是答案。
CommandGroup(before: CommandGroupPlacement.toolbar) {
    Toggle("Show X", isOn: $model.settings.showX)
    Toggle("Show Y", isOn: $model.settings.showY)
    Divider()
}

0

我发现SwiftUI中唯一删除默认菜单项的方法是通过以下类实现:

class TheApp{
    static func removeUnnecessaryMenus() {
        if let menu = NSApplication.shared.mainMenu {
            menu.items.removeFirst{ $0.title == "Edit" }
            menu.items.removeFirst{ $0.title == "File" }
            menu.items.removeFirst{ $0.title == "Window" }
            menu.items.removeFirst{ $0.title == "View" }
        }
    }
}

并在一些视图的 init() 函数中调用 TheApp.removeUnnecessaryMenus()

这是一个糟糕的解决方案,但我在 SwiftUI 2 中找不到其他替代方法。


如果你在didFinishLaunching()中移除它们,那么这不是一个坏的解决方案。 - arthas
1
@arthas 在这种情况下将无法按预期工作。因为这些菜单将在每次刷新菜单时重新添加。=( 这就是为什么我说这是一个不好的解决方案。 - Andrew_STOP_RU_WAR_IN_UA
2
是的,我也遇到过这个问题。但是有一个更简单的解决方案,就是创建一个NSMenu子类并将其分配给mainMenu。https://medium.com/@theboi/macos-apps-without-storyboard-or-xib-menu-bar-in-swift-5-menubar-and-toolbar-6f6f2fa39ccb - arthas

0
extension AppDelegate {
    func applicationWillUpdate(_ notification: Notification) {
        DispatchQueue.main.async {
            guard let currentMainMenu = NSApplication.shared.mainMenu else { return }

            // remove some items that cannot be deleted from SwiftUI
            ["Edit", "File", "Window"].forEach { name in
                currentMainMenu.item(withTitle: name).map { NSApp.mainMenu?.removeItem($0) }
            }
            
            guard let menu = currentMainMenu.item(withTitle: "View")?.submenu else { return }
            // remove sub-items of "View"
            menu.removeAllItems()
            
            //add new items
            menu.addItem(self.recentsPlusFavorites)
            menu.addItem(self.recentsMini)
            menu.addItem(self.recentsLarger)
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接