xcode SwiftUI更新主菜单[已解决] kludgey

omqzjyyz  于 2022-11-17  发布在  Swift
关注(0)|答案(1)|浏览(180)

真实的的问题
如何更新SwiftUI中的mainMenu,使其真正工作?
我已经在SwiftUI中构建了一个基于MacOS文档的应用程序,其中包括所有内置的文件菜单命令(即关闭、保存、复制、重命名等)
在保存文档之前,我验证了结构,如果有任何验证错误,我希望向用户显示一个模式对话框。
模态对话框只是一个简单的确定/取消对话框-“确定”表示用户愿意保存有验证错误的文件,“取消”则需要停止保存操作。
所以问题是:“如何截取内置的”保存“菜单命令以显示此对话框?
我尝试过覆盖.saveItem CommandGroup -但这会替换所有菜单项,我只想覆盖几个命令(“保存”和“另存为”),而不想重新实现所有命令(我不确定我是否有这样做的技能)

.commands {
            CommandGroup(replacing: .saveItem) {
                // code goes here - but removes all of the in-built menus
            }
        }

我已尝试过此解决方案(In a SwiftUI Document App, how to save a document from within a function
并将其放入我的AppDelegate中

public func applicationDidBecomeActive(_ notification: Notification) {
        let menu = NSApplication.shared.mainMenu!.items.first(where: { $0.title == "File" })!
        let submenu = menu.submenu!.items.first(where: { $0.title == "Save" })!
        submenu.action = #selector(showDialog)
    }

    @objc func showDialog() {
        var retVal: Int = 0

        let thisWindow: NSWindow? = NSApplication.shared.mainWindow

        let nsAlert: NSAlert = NSAlert()
        let cancelButton: NSButton = nsAlert.addButton(withTitle: "Cancel")
        cancelButton.tag = 1

        let okButton: NSButton = nsAlert.addButton(withTitle: "OK")
        okButton.tag = 0

        // The below code is replaced
        nsAlert.beginSheetModal(for: thisWindow!) { modalResponse in
            print(modalResponse)
            retVal = modalResponse.rawValue

            if retVal == 0 {
                print("save")
            } else {
                print("cancel")
            }
        }
    }

然而,它实际上并不调用showDialog函数。

编辑/更新

更新菜单仍然有困难,但在上面的例子中,调用beginModalSheet是不正确的,因为进程将在后台运行。更新了对runModal()的调用,这将停止任何后台进程写入文件。

@objc func showDialog() {
        let nsAlert: NSAlert = NSAlert()
        let cancelButton: NSButton = nsAlert.addButton(withTitle: "Cancel")
        cancelButton.tag = 1

        let okButton: NSButton = nsAlert.addButton(withTitle: "OK")
        okButton.tag = 0

        let response: Int = nsAlert.runModal().rawValue
        if response == 0 {
            print("save")
            NSApp.sendAction(#selector(NSDocument.save(_:)), to: nil, from: nil)
        } else {
            print("cancel")
        }
    }

我在某个地方读到过,您需要在窗口出现之前设置菜单,我还读到过,您需要在设置AppDelegate之前设置菜单。

又一次编辑

查看此帖子Hiding Edit Menu of a SwiftUI / MacOS app
和此注解

想法:SwiftUI可能有一个bug,或者他们真的不想让你删除NSApp. mainMenu中的顶级菜单。SwiftUI似乎重置了整个菜单,目前没有办法覆盖或自定义大多数细节(Xcode 13.4.1)。CommandGroup(替换:.textEditing){ }-esque命令不允许您删除或清除整个菜单。分配一个新的NSApp.mainMenu只会在SwiftUI需要时被破坏,即使您没有指定任何命令。

abithluo

abithluo1#

一月一日****一月一日

经过大量的超级令人沮丧的搜索尝试和大量的代码-我减少了问题,只是试图改变名称的save菜单项-如果我可以做到这一点-那么我可以改变它的行动以及。
我是这样做的

  • 我的Tetsing应用程序名为YikesRedux *

操作步骤:
1.注册AppDelegate
1.覆写applicationWillUpdate方法
1.将菜单更新放在DispatchQueue.main.async闭包中
1.经过几天的搜索,你终于解决了这个问题,喜极而泣吧
YikesAppRedux.swift

import SwiftUI

@main
struct YikesReduxApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate // <- Don't forget the AppDelegate

    var body: some Scene {
        DocumentGroup(newDocument: YikesReduxDocument()) { file in
            ContentView(document: file.$document)
        }
    }
}

AppDelegate.swift

import Foundation
import AppKit

public class AppDelegate: NSObject, NSApplicationDelegate {
    public func applicationWillUpdate(_ notification: Notification) {
        DispatchQueue.main.async {
            let currentMainMenu = NSApplication.shared.mainMenu

            let fileMenu: NSMenuItem? = currentMainMenu?.item(withTitle: "File")
            if nil != fileMenu {
                let saveMenu = fileMenu?.submenu!.item(withTitle: "Save")
                if nil != saveMenu {
                    print("updated menu")
                    saveMenu?.title = "Save Updated"
                }
            }
        }
    }
}

我把这个记下来作为一个有点kludgey -因为它运行在每个应用程序更新(这不是很多,但你可以看到打印出来的控制台“更新菜单”时,它确实发生)
我确实尝试过保留一个状态变量来指示菜单是否更新,尝试不再这样做--但在多文档窗口环境中,您需要跟踪每个窗口... (此外,swift只会在需要的时候对菜单进行重命名--因此它没有像预期的那样工作。)
我把菜单更新代码放在我能想到的几乎所有地方

  • 每个AppDelegate函数覆盖
  • AppContentView的初始化方法
  • 文件读取功能/写入功能
  • 你说得出的-我把它放在那里(我甚至有一个托管控制器,一个NSViewRepresentable)

然后我把它们一个一个地移走,直到我找到了解决办法。
如果有一种不那么笨拙的方法来做这件事,我会很高兴的。

相关问题