Xcode 如何摆脱MacOS中自动添加的菜单项?

Xcode 如何摆脱MacOS中自动添加的菜单项?,xcode,macos,menu,Xcode,Macos,Menu,在MacOS顶部菜单栏的Edit部分,它不断添加Start Dictation…和Emoji&Symbols,即使它不在其中。 最后两个,不想要的东西用红色圈起来。我不能删除它们,因为它们在Xcode UI构建器中不存在,但它们会以某种方式自动添加。我怎样才能摆脱它们 我不允许在应用程序启动时以编程方式删除它们。我首先需要预防它们 我试图用一个新创建的菜单替换当前的编辑菜单,删除旧菜单,并转移项目,但没有成功。我还试着把它改名为另一个名字,但也没有成功 我的代码是用Swift编写的,因此ObjC

在MacOS顶部菜单栏的
Edit
部分,它不断添加
Start Dictation…
Emoji&Symbols
,即使它不在其中。 最后两个,不想要的东西用红色圈起来。我不能删除它们,因为它们在Xcode UI构建器中不存在,但它们会以某种方式自动添加。我怎样才能摆脱它们

我不允许在应用程序启动时以编程方式删除它们。我首先需要预防它们

我试图用一个新创建的菜单替换当前的编辑菜单,删除旧菜单,并转移项目,但没有成功。我还试着把它改名为另一个名字,但也没有成功

我的代码是用Swift编写的,因此ObjC答案对我没有帮助,而且在程序以编程方式启动后,我不允许手动删除它们


提前谢谢

我发布了一个答案,使用Swift和选择器来回答Willeke链接的问题。我不会在这里重复这个解决方案,但在最后我提到了另一种方法,我将在这里详细介绍

首先,我要说这个解决方案有点重,但它对苹果可能选择添加的任何未来自动菜单也很强大。它最适合以编程方式创建菜单。你可以在故事板上使用它,但这更痛苦

这样做的目的是在不依赖未记录的
UserDefaults
设置的情况下,首先防止添加不需要的菜单

要实现这一点,您需要控制添加菜单项的过程。这发生在
NSMenu
中,因此计划对其进行子类化,覆盖各种
addItem
/
insertItem
方法,以检查正在添加的任何
NSMenuItem
标记
属性。如果标签与您为应用程序定义的某个值不匹配,只需拒绝添加该项即可

不幸的是,
NSMenu
在调用
addItem(withTitle:action:keyEquivalent)
时,也没有调用
insertItem
方法,因此必须重写所有方法,而不仅仅是两个方法

进行一些调试打印也很有帮助,尤其是在使用故事板时,因为很容易忽略标记菜单项

class TaggedItemMenu: NSMenu
{
    static let wantedTag = 42 // or whatever value
    
    // Helper for creating properly tagged menu items
    private func makeTaggedItem(
        withTitle string: String,
        action selector: Selector?,
        keyEquivalent charCode: String) -> NSMenuItem
    {
        let newItem = NSMenuItem(
            title: string,
            action: selector,
            keyEquivalent: charCode
        )
        newItem.tag = Self.wantedTag
        return newItem
    }
    
    // If you use Storyboards, you have to individually set all the tags, so
    // its helpful to log untagged add/inserts so you can check they're not one
    // of your menu items you missed setting the tag for.
    private func logUntaggedAddInsert(
        _ item: @autoclosure () -> NSMenuItem,
        function: StaticString = #function)
    {
        #if DEBUG
        print("Call to \(function) for untagged NSMenuItem named \"\(item().title)\"")
        #endif
    }

    // MARK: Methods for your app to use
    // -------------------------------------
    public override func addItem(_ newItem: NSMenuItem)
    {
        guard newItem.tag == Self.wantedTag else
        {
            logUntaggedAddInsert(newItem)
            return
        }
        super.addItem(newItem)
    }
    
    // Replacement for addItem(withTitle:action:keyEquivalent)
    public func addTaggedItem(
        withTitle string: String,
        action selector: Selector?,
        keyEquivalent charCode: String) -> NSMenuItem
    {
        let newItem = makeTaggedItem(
            withTitle: string,
            action: selector,
            keyEquivalent: charCode
        )
        super.addItem(newItem)
        return newItem
    }
    
    public override func insertItem(_ newItem: NSMenuItem, at index: Int)
    {
        guard newItem.tag == Self.wantedTag else
        {
            logUntaggedAddInsert(newItem)
            return
        }
        super.insertItem(newItem, at: index)
    }

    // Replacement for insertItem(withTitle:action:keyEquivalent:at)
    public func insertTaggedItem(
        withTitle string: String,
        action selector: Selector?,
        keyEquivalent charCode: String,
        at index: Int) -> NSMenuItem
    {
        let newItem = makeTaggedItem(
            withTitle: string,
            action: selector,
            keyEquivalent: charCode
        )
        
        super.insertItem(newItem, at: index)
        return newItem
    }

    // MARK: Do NOT use these methods in your app
    // These will be used when macOS automatically inserts menus items.
    // -------------------------------------
    public override func addItem(
        withTitle string: String,
        action selector: Selector?,
        keyEquivalent charCode: String) -> NSMenuItem
    {
        let newItem = NSMenuItem(
            title: string,
            action: selector,
            keyEquivalent: charCode
        )
        logUntaggedAddInsert(newItem)
        return newItem
    }
    
    public override func insertItem(
        withTitle string: String,
        action selector: Selector?,
        keyEquivalent charCode: String,
        at index: Int) -> NSMenuItem
    {
        let newItem = NSMenuItem(
            title: string,
            action: selector,
            keyEquivalent: charCode
        )
        logUntaggedAddInsert(newItem)
        return newItem
    }
}
如果以编程方式创建菜单,则只创建
TaggedItemNu
s而不是
NSMenu
,并确保只使用
addTaggedItem
insertTaggedItem
创建菜单项。这样,自动添加的菜单一开始就不会进入您的菜单,之后您无需担心删除它们

还要记住,当您添加子菜单时,它们基本上是包装在菜单项中的菜单。子菜单的菜单部分也需要是一个
TaggedItemMenu
,并且它包装在中的
NSMenuItem
需要标记,否则不会添加。无论您是否需要标记菜单项,都可以通过
扩展名
addSubmenu
insertSubmenu
添加到
NSMenu
(或者在本例中添加到
TaggedItemMenu
),为您进行包装

如果您使用的是故事板,则必须确保在其“标识检查器”中将每个菜单和子菜单的类更改为
TaggedItemMenu

并在其“属性检查器”中为每个菜单项单独设置标记

在你开始计算菜单和子菜单中的所有项目之前,这似乎并不太糟糕,所有这些都只是为了去除苹果决定注入到你程序中的几个项目。当然,如果以后添加新的菜单项,则需要确保设置它们的
标记。这就是为什么日志记录会派上用场


如果您无法使用自动添加的菜单项,我建议您要么远离情节提要,要么采取其中一种方法在事后删除这些菜单项,并接受它可能会在未来的macOS版本中崩溃。

为什么需要删除这些选项?它很可能是操作系统的一个底层功能,超出了您的控制范围。依赖这些选项(尤其是听写)的用户可能需要它们。这是否回答了您的问题@DavidMordigal如果我将编辑作为一个整体删除,这些内容就会消失,但如果我想要一个只有复制和粘贴功能的简单菜单呢?@Willeke No but close。我的应用程序是Swift,而不是ObjC,并且没有合适的Swift答案