我如何在 Cocoa 中获取应用程序菜单

Posted

技术标签:

【中文标题】我如何在 Cocoa 中获取应用程序菜单【英文标题】:How i can get the Application Menu in Cocoa 【发布时间】:2011-03-27 01:40:22 【问题描述】:

如何获取应用程序菜单的 NSMenu 或 NSMenuItem(在苹果菜单旁边的菜单栏中)。它似乎是自动创建的,并且独立于我通过 NSApplication setMainMenu 设置的 NSMenu。

顺便说一句:我在没有 Xcode 的情况下构建我的完整应用程序,所以请不要使用 InterfaceBuilder 提示。

PS:MacOSX 10.5

【问题讨论】:

【参考方案1】:

没有 IB,您可以使用 NSApplication 的 mainMenu 访问菜单:

NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
NSMenu *appMenu = [[mainMenu itemAtIndex:0] submenu];

for (NSMenuItem *item in [appMenu itemArray]) 
    NSLog(@"%@", [item title]);

【讨论】:

不,这不起作用。当我清楚地看到菜单栏中的“TestMenu File”项目时,NSMenu 中的 numberOfItems 也给了我 1。 啊,我似乎对您的项目结构做出了假设。您是否在 XCode 中创建了项目,然后完全删除了 nib/xib?还是你完全从头开始?这篇博文讨论了您对 mainMenu 的体验以及您如何能够以这样的方式工作您的项目以获得应用程序菜单:lapcatsoftware.com/blog/2007/05/16/working-without-a-nib-part-1 谢谢。是的,我完全没有头脑,事实上,在开发过程中,我什至作为没有 Bundle 结构的普通 ELF a.out 程序运行。除了菜单,它工作正常。 如何在主菜单中添加一个新的菜单项? [appMenu addItem:newMenuItem];或 [appMenu insertItem:newMenuItem atIndex:1];不工作。【参考方案2】:

虽然这是 5 年前的问题......我喜欢分享如何制作它。

根据我在使用 Xcode 7.1 的 OS X 10.11 (El Capitan) 的经验,复制该应用程序菜单并不难。 Apple 似乎消除了所有奇怪的限制。

注意:此代码针对 Swift 3 进行了更新,并且仅在 macOS Sierra (10.12.1) 中进行了测试。

//
//  AppDelegate.swift
//  Editor6MainMenuUI2Testdrive
//
//  Created by Hoon H. on 2016/11/05.
//  Copyright © 2016 Eonil. All rights reserved.
//

import Cocoa

/// You SHOULD NOT use `@NSApplicationMain` 
/// to make your custom menu to work.
class AppDelegate: NSObject, NSApplicationDelegate 
    func applicationDidFinishLaunching(_ aNotification: Notification) 
    func applicationWillTerminate(_ aNotification: Notification) 


func makeMainMenu() -> NSMenu 
    let mainMenu            = NSMenu() // `title` really doesn't matter.
    let mainAppMenuItem     = NSMenuItem(title: "Application", action: nil, keyEquivalent: "") // `title` really doesn't matter.
    let mainFileMenuItem    = NSMenuItem(title: "File", action: nil, keyEquivalent: "")
    mainMenu.addItem(mainAppMenuItem)
    mainMenu.addItem(mainFileMenuItem)

    let appMenu             = NSMenu() // `title` really doesn't matter.
    mainAppMenuItem.submenu = appMenu

    let appServicesMenu     = NSMenu()
    NSApp.servicesMenu      = appServicesMenu

    appMenu.addItem(withTitle: "About Me", action: nil, keyEquivalent: "")
    appMenu.addItem(NSMenuItem.separator())
    appMenu.addItem(withTitle: "Preferences...", action: nil, keyEquivalent: ",")
    appMenu.addItem(NSMenuItem.separator())
    appMenu.addItem(withTitle: "Hide Me", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h")
    appMenu.addItem( () -> NSMenuItem in
        let m = NSMenuItem(title: "Hide Others", action: #selector(NSApplication.hideOtherApplications(_:)), keyEquivalent: "h")
        m.keyEquivalentModifierMask = [.command, .option]
        return m
        ())
    appMenu.addItem(withTitle: "Show All", action: #selector(NSApplication.unhideAllApplications(_:)), keyEquivalent: "")

    appMenu.addItem(NSMenuItem.separator())
    appMenu.addItem(withTitle: "Services", action: nil, keyEquivalent: "").submenu = appServicesMenu
    appMenu.addItem(NSMenuItem.separator())
    appMenu.addItem(withTitle: "Quit Me", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")

    let fileMenu = NSMenu(title: "File")
    mainFileMenuItem.submenu = fileMenu
    fileMenu.addItem(withTitle: "New...", action: #selector(NSDocumentController.newDocument(_:)), keyEquivalent: "n")

    return mainMenu


let del = AppDelegate()
/// Setting main menu MUST be done before you setting app delegate.
/// I don't know why.
NSApplication.shared().mainMenu = makeMainMenu()
NSApplication.shared().delegate = del
NSApplication.shared().run()

无论如何,它不是自动生成的,我必须自己设置它们。我不确定是否有其他方法可以做到这一点。

您可以下载工作示例here。

【讨论】:

注意:关键点,在这个答案中,新创建的菜单必须在设置应用程序委托之前分配给 NSApp.mainMenu 。稍后尝试分配,例如applicationDidFinishLaunching 就像我试图做的那样不起作用。【参考方案3】:

Swift 5.0 我的两分钱

private final func manageMenus()
    let  mainMenu =  NSApplication.shared.mainMenu

    if let editMenu = mainMenu?.item(at: 1)?.submenu
        for item in editMenu.items
            print(item.title)
        
    

所以你也可以启用它:

....

  for item in editMenu.items
       item.isEnabled = true
   

【讨论】:

【参考方案4】:

在没有 Xcode 或 IB 的情况下制作 Cocoa 应用程序对我来说听起来很自虐,但对每个人来说都是自虐...试试这个:[[[NSApp mainMenu] itemAtIndex: 0] submenu]

【讨论】:

好吧,我正在编写语言绑定并希望保持平台可移植性。但是您的解决方案不起作用。具有自动生成的包名称的菜单项不是 mainMenu 的一部分。 它对我有用,但我在带有 Xcode 和 IB 的普通应用程序中尝试过。 是不是你在应用启动过程中尝试过早?我在applicationDidFinishLaunching:委托方法中试过了。 不,我在窗口内按下按钮时检查了它。我只能猜测 NSMenu 和 NSMenuItem 的名称有一些魔力,Apple 正在使用名称或标签的一些魔力(例如将菜单项添加到具有特定名称的菜单中),但我想我尝试了明显的那些。跨度> 您可以随意命名 IB 中的第一个菜单,它会在运行时具有正确的名称。您是自己创建该菜单,还是仅创建文件、编辑等菜单?

以上是关于我如何在 Cocoa 中获取应用程序菜单的主要内容,如果未能解决你的问题,请参考以下文章

如何以编程方式创建 Cocoa 应用程序主菜单应用程序 [关闭]

带有菜单栏但没有 Dock 图标/切换菜单的 Cocoa 应用程序

如何在 Cocoa 应用程序中切换视图?

Cocoa 在多个窗口中验证菜单项

如何为 Mac 创建 Cocoa App 首选项?

无法使用 Cocoa 中的服务在 Finder 的上下文菜单中添加项目