16

How can I get the NSMenu or NSMenuItem for the application menu (the one in the menu bar next to the apple menu). It seems to be automatically created and independent from the NSMenu I set via NSApplication setMainMenu.

By the way: I'm building my complete application without Xcode, so please no InterfaceBuilder tips.

PS: MacOSX 10.5

idmean
  • 14,540
  • 9
  • 54
  • 83
Lothar
  • 12,537
  • 6
  • 72
  • 121

4 Answers4

22

Without IB, you can access the menu using the NSApplication's mainMenu:

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

for (NSMenuItem *item in [appMenu itemArray]) {
    NSLog(@"%@", [item title]);
}
Jarret Hardie
  • 95,172
  • 10
  • 132
  • 126
  • No this does not work. numberOfItems in NSMenu also gives me 1 when i clearly see the "TestMenu File" items in the menubar. – Lothar Aug 08 '10 at 23:18
  • Ah, I appear to have made an assumption about your project structure. Did you create the project in XCode and then remove the nib/xib completely? Or are you totally going from scratch? This blog entry talks about what you're experiencing with the mainMenu and how you might be able to work your project in such a way to get the application menu: http://lapcatsoftware.com/blog/2007/05/16/working-without-a-nib-part-1/ – Jarret Hardie Aug 09 '10 at 01:13
  • Thanks thats it. And yes i'm completely nibless, in fact during development i even run as a normal ELF a.out program without a Bundle structure. It works fine execept for the menu. – Lothar Aug 09 '10 at 07:54
  • How to add a new menu item to the mainMenu? [appMenu addItem:newMenuItem]; or [appMenu insertItem:newMenuItem atIndex:1]; is not working. – aye2m Mar 27 '17 at 10:05
18

Though this is 5 years old question... I like to share how to make it.

In my experience in OS X 10.11 (El Capitan) with Xcode 7.1, it's not hard to replicate that application menu. It seems Apple removed all the weird limitations.

Note: This code is updated for Swift 3, and tested only in 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()

Anyway, it is not being generated automatically, and I had to set them all up myself. I am not sure whether there is another way to do this or not.

You can download working example here.

eonil
  • 83,476
  • 81
  • 317
  • 516
  • NB: Key point, in this answer is that the newly created menu has to be assigned to NSApp.mainMenu before the app delegate is set. Trying to assign later in e.g. applicationDidFinishLaunching as I was trying to do just doesn't work. – shufflingb Feb 17 '22 at 14:45
5

my two cents for 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)
        }
    }
}

so You can also enable it:

....

  for item in editMenu.items{
       item.isEnabled = true
   }
ingconti
  • 10,876
  • 3
  • 61
  • 48
1

Making a Cocoa app without Xcode or IB sounds masochistic to me, but to each his own... Try this: [[[NSApp mainMenu] itemAtIndex: 0] submenu].

JWWalker
  • 22,385
  • 6
  • 55
  • 76
  • Well i'm writting a language binding and want to stay platform portable. But your solution does not work. The menu item with the bundle name that is autogenerated is not part of the mainMenu. – Lothar Aug 08 '10 at 23:16
  • It worked for me, but I tried it in a normal app with Xcode and IB. – JWWalker Aug 08 '10 at 23:25
  • Is it possible that you tried it too early in the app startup process? I tried it in the `applicationDidFinishLaunching:` delegate method. – JWWalker Aug 08 '10 at 23:29
  • No i checked it while pressing a button inside the window. I can only guess that there is some magic with the names of NSMenu and NSMenuItem, Apple is using some magic with names or tags (like the addition of menu items to menus with specific names) but i think i tried the obvious ones. – Lothar Aug 08 '10 at 23:50
  • You can name the first menu in IB whatever you want and it will have the correct name at run time. Are you creating that menu yourself, or are you only creating the File, Edit, etc. menus? – Peter Hosey Aug 09 '10 at 01:09
  • I only create File and Edit. The application menu or maybe just the NSMenuItem in the menu bar is created automagically by Cocoa - well otherwise i would have a pointer to it. – Lothar Aug 09 '10 at 01:24
  • In the templates that come with Xcode, there is an application menu in the nib's main menu along with the File and Edit menus. You should create the application menu yourself. – Peter Hosey Aug 09 '10 at 03:10