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

16

如何获取应用程序菜单(即苹果菜单旁边的菜单栏中的那个)的NSMenu或NSMenuItem?它似乎是自动创建的,与我通过NSApplication setMainMenu设置的NSMenu无关。

顺便说一下:我正在构建我的完整应用程序,没有使用Xcode,请勿提供InterfaceBuilder提示。

PS:MacOSX 10.5

4个回答

22

如果没有使用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。 - Lothar
啊,我似乎对你的项目结构做出了一些假设。你是在XCode中创建项目,然后完全删除了nib/xib吗?还是你完全从头开始?这篇博客文章讨论了关于mainMenu的问题,以及如何通过某种方式来处理你的项目,以获得应用程序菜单:http://lapcatsoftware.com/blog/2007/05/16/working-without-a-nib-part-1/ - Jarret Hardie
谢谢,就这些。是的,我完全没有 nib 文件,事实上在开发过程中,我甚至作为一个普通的 ELF a.out 程序运行,没有 Bundle 结构。它工作得很好,除了菜单。 - Lothar
如何向mainMenu添加新菜单项?[appMenu addItem:newMenuItem]; 或[appMenu insertItem:newMenuItem atIndex:1]; 不起作用。 - aye2m

18

虽然这是一个5年前的问题...但我想分享一下如何制作它。

在我的经验中,使用OS X 10.11(El Capitan)和Xcode 7.1,在应用程序菜单上复制这个功能并不难。似乎苹果已经移除了所有奇怪的限制。

注意:该代码已更新为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()

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

您可以在此处下载工作示例


重点是,在设置应用程序委托之前,新创建的菜单必须分配给NSApp.mainMenu。尝试稍后在applicationDidFinishLaunching中分配(就像我尝试做的那样)是无效的。 - shufflingb

5

我对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
   }

1
制作 Cocoa 应用程序而不使用 Xcode 或 IB 对我来说听起来有点自虐,但每个人都有自己的选择... 试试这个:[[[NSApp mainMenu] itemAtIndex: 0] submenu]

它对我有效,但我在Xcode和IB中的普通应用程序中尝试了它。 - JWWalker
你是不是在应用程序启动过程中尝试得太早了?我是在applicationDidFinishLaunching:代理方法中尝试的。 - JWWalker
我在窗口内按下一个按钮时检查了它,但仍无法确定。我猜想NSMenu和NSMenuItem的名称可能存在某些魔法,苹果公司使用名称或标签的一些魔法(例如将菜单项添加到特定名称的菜单中),但我认为我已经尝试了显而易见的那些。 - Lothar
你可以随意命名IB中的第一个菜单,它在运行时会有正确的名称。你是自己创建那个菜单,还是只创建文件、编辑等菜单? - Peter Hosey
在 Xcode 自带的模板中,nib 的主菜单中有一个应用程序菜单,以及文件和编辑菜单。您应该自己创建应用程序菜单。 - Peter Hosey
显示剩余2条评论

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