Im surprised that this hasn't already been asked:
But how does one go about changing the NSMenuItem
title in a NSStatusBar
menu. When a user logs in I want the menu item to say logout. I have tried creating an outlet to modify my NSMenuItem
as a would a label or something.
AppDelegate.h
@property (retain) IBOutlet NSMenuItem *loginItem;
AppDelegate.m
[loginItem setTitle:@"Logout"];
But that didnt work.
The only thing that I was able to do was delete the old NSMenuItem
, then add a new one, but it would just add it to the bottom. Is the only way to do this to remove every menu item then re-add them?? That seems very inefficient.
The method you describe should work, though, in general, keeping IBOutlet
s for all your menu items can be tedious. (If your solution isn't working, make sure the IBOutlet
is actually connected in the nib file, and make sure that you're setting the title at an appropriate time. If you're trying to set it in your controller's init
method, for example, that's too early on, and the outlets haven't yet been connected up: move the method to awakeFromNib
or similar.
A better approach in the long run is to use the <NSMenuDelegate>
protocol and NSMenuValidation
(informal) protocol to update menu items dynamically (and lazily).
For example, define your controller class like the following:
@interface MDAppDelegate : NSObject <NSApplicationDelegate, NSMenuDelegate>
@property (strong) NSStatusItem *statusItem;
@property (weak) IBOutlet NSWindow *window;
@property (weak) IBOutlet NSMenu *statusItemMenu;
@property (weak) IBOutlet NSMenuItem *toggleLoginLogoutMenuItem;
@property (weak) IBOutlet NSTextField *statusField;
@property (weak) IBOutlet NSTextField *progressField;
@property (weak) IBOutlet NSProgressIndicator *progressIndicator;
@property (assign) BOOL loggedIn;
- (IBAction)toggleLoginLogout:(id)sender;
@end
In the nib file, the delegate
outlet of the statusItemMenu
is set to the MDAppDelegate
controller class. That assures that the MDAppDelegate
class is in the responder chain and allow it to work with validating the menu items.
Then you could implement your .m like the following:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
_statusItem.menu = _statusItemMenu;
_statusItem.title = NSLocalizedString(@"NSStatusItem", @"");
[self updateLoggedInStatus];
}
- (void)updateLoggedInStatus {
[self.statusField setStringValue:(self.loggedIn ? @"Logged in" : @"Logged out")];
}
- (IBAction)toggleLoginLogout:(id)sender {
[self performSelector:@selector(finishFakeLoginLogout:)
withObject:nil afterDelay:2.0];
}
- (void)finishFakeLoginLogout:(id)sender {
self.loggedIn = !self.loggedIn;
[self updateLoggedInStatus];
}
- (void)menuNeedsUpdate:(NSMenu *)menu {
#if MD_DEBUG
NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
#endif
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
#if MD_DEBUG
NSLog(@"[%@ %@]", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
#endif
SEL action = menuItem.action;
if (action == @selector(toggleLoginLogout:)) {
[menuItem setTitle:(self.loggedIn ? @"Logout" :@"Login")];
}
return YES;
}
Sample project: http://github.com/NSGod/NSStatusBarFinagler