7

I am trying to change the titles of some of the items in my Cocoa app's main menu. I have tried setting them both within IB and also programmatically from my app's applicationDidFinishLaunchingWithOptions: method. Either way, the title property of my NSMenuItem object does change. But none of my changes are reflected in the actual title of the item at the top of the screen when the app is running.

Can anyone explain what is going on? And how can I change this?

EDIT: The data structure is the default one that IB sets up:

NSApplication *app = [NSApplication sharedApplication];
NSMenu *mainMenu = [app mainMenu];
NSArray *itemArray = [mainMenu itemArray];
NSMenuItem *firstItem = [itemArray objectAtIndex: 0];
NSMenu *submenu = [firstItem submenu];

I have changed the title properties of both firstItem and subMenu to be my desired title. Yet the default one still shows.

William Jockusch
  • 26,513
  • 49
  • 182
  • 323
  • Are they standalone `NSMenuItem` instances or are they `NSMenu` instances? `NSMenu` keeps its own title. –  Feb 11 '11 at 05:12
  • 1
    The main menu of an application shown in the menu bar at the top of the screen is an NSMenu; but each of its visible entries - e.g. "App Name", "File", "Edit", "View" etc. - is an NSMenuItem. Each menu item contains an NSMenu which is the submenu you see dropping down. So it's an NSMenu, but it's a "special one" which displays horizontally in the menu bar rather than vertically. Now, I would expect you to be able to double-click on items in the simulated menu bar shown when you load the NIB into Interface Builder and set the text directly there. Does this not work for you? – Andrew Hodgkinson Feb 11 '11 at 10:52
  • Andrew -- I can change the text, but it still shows the same in the app. – William Jockusch Feb 12 '11 at 17:24
  • Someone from Codeweavers said that they use a "ugly hack" to do this, where they change the app bundle name before AppKit gets to see it. I don't have details. – tbodt Jun 14 '15 at 21:34

5 Answers5

7

Instead of renaming project change "Bundle name" in your-application-Info.plist from ${PRODUCT_NAME}; to whatever you want; this change will be reflected in the title of Application menu item of Main Menu.

askh
  • 101
  • 1
  • 3
6

Sometimes, this just does not work, because Cocoa doesn't like your title :-p That happens e.g. when the title you chose is the localized app name, but it wants to display the non-localized app name. A little trick can help...

NSMenu *menu = [[[NSApp mainMenu] itemAtIndex:0] submenu];
NSString *title = @"My app name";

// Append some invisible character to title :)
title = [title stringByAppendingString:@"\x1b"];

[menu setTitle:title];

Yeah, it's crazy, but that extra character at the end made all the difference. (You may also append just a SPACE, but then the menu item grows, which is probably not what you want.)

Tested on OS X 10.9.5.

Another thing:

You have to do all of this AFTER you have showed a window. Otherwise it just does not work. Furthermore if you do this procedure at app start, and then later when the window is shown do it again, it may not work too.

Michael
  • 6,451
  • 5
  • 31
  • 53
  • 2
    I did this in - (void)applicationDidFinishLaunching:(NSNotification *)aNotification and it worked just fine. – Paul Linsay Mar 31 '15 at 12:58
  • 1
    This does work, but the title is not in bold font anymore? – basdp Mar 04 '18 at 10:38
  • @basdp: is this a question? or did you have the problem that the title is not in bold font anymore? Everywhere I have tested it, the title remained in bold. – Michael Mar 13 '18 at 21:41
  • Yes, I experience the same. First menu title (ex app name) in menu bar changes, but not in bold anymore. I've tried to set the font, no result. Code is: let items = NSApplication.shared.mainMenu?.items let firstItem = items?[0] let subMenu = firstItem?.submenu subMenu?.title = "NewApplication " subMenu?.font = NSFont.boldSystemFont(ofSize: 0) – claude31 Nov 22 '19 at 08:13
0

Renaming the project did it. Seems too extreme, though. I would still be interested in a less extreme solution.

William Jockusch
  • 26,513
  • 49
  • 182
  • 323
0

Better late than never: after digging around I've found a working solution, even two!

You can set a menu image with a rendered text (you can use bold font!)

// render a string into image
let string = NSString(string: "Application Name")
let font = NSFont.boldSystemFont(ofSize: 13)
let size = string.size(withAttributes: [.font: font])
let image = NSImage(size: size)
image.lockFocus()
let rect = NSRect(origin: NSPoint(x: 0, y: 0.5), size: size)
string.draw(with: rect, options: [.usesLineFragmentOrigin], attributes: [.font: font])
image.unlockFocus()

appMenuItem.submenu?.title = " " // clear original App Name
appMenuItem.image = image // set the image instead

Alternatively you may change the whole Process Name including the Apple Menu title (thanks to this answer!)

// dynamically load ApplicationServices.GetCurrentProcess
typealias GetCurrentProcessType = @convention(c) (UnsafePointer<ProcessSerialNumber>) -> OSStatus
let getCurrentProcessSym = dlsym(UnsafeMutableRawPointer(bitPattern: -2), "GetCurrentProcess")!
let getCurrentProcess = unsafeBitCast(getCurrentProcessSym, to: GetCurrentProcessType.self)

// dynamically load ApplicationServices.CPSSetProcessName
typealias CPSSetProcessNameType = @convention(c) (UnsafePointer<ProcessSerialNumber>, UnsafePointer<Int8>) -> OSStatus
let cpsSetProcessNameSym = dlsym(UnsafeMutableRawPointer(bitPattern: -2), "CPSSetProcessName")!
let cpsSetProcessName = unsafeBitCast(cpsSetProcessNameSym, to: CPSSetProcessNameType.self)

// Get current process serial number
var psn = ProcessSerialNumber()
getCurrentProcess(&psn)
// Change Process Name
"Custom Title".withCString { cpsSetProcessName(&psn, $0) }
0

There is actually a better way to address this issue. There will be a Main.strings file in your project which contains all the strings for MenuItems for the application, you can directly go there and update as per your requirements. This way we can also support localization with out code level changes.

Trident
  • 810
  • 9
  • 20