mac status bar application not working - objective-c

I'm following this: http://lepture.com/en/2012/create-a-statusbar-app simple tutorial to get a Status Bar based Mac app working, I've referenced Apple's NSStatusItem class reference as well - And cannot figure out what I'm doing wrong?
It's just not working. My project uses ARC.
Here's FPAppDelete.h:
#import <Cocoa/Cocoa.h>
#interface FPAppDelegate : NSObject <NSApplicationDelegate>
#property (weak) IBOutlet NSMenu *statusMenu;
#property (strong, nonatomic) NSStatusItem *statusBar;
#end
Here's FPAppDelegate.m:
#import "FPAppDelegate.h"
#implementation FPAppDelegate
#synthesize statusBar = _statusBar;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
}
- (void) awakeFromNib {
self.statusBar = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
self.statusBar.title = #"G";
self.statusBar.menu = self.statusMenu;
self.statusBar.highlightMode = YES;
}
#end
I'm not expecting this at all, but I get this when I run the app, with nothing in my Status Bar

It looks like you are willing to listen. So I'll show you quickly how to run a status application.
(1) Add NSMenu to the side bar (or whatever you call). (See the screenshot below.) It's up to you to keep or remove 3 generic menu items.
(2) Add the following instance variables under AppDelegate.h.
#interface AppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSMenu *statusMenu;
NSStatusItem *statusItem;
NSImage *statusImage;
}
#property (assign) IBOutlet NSWindow *window;
#property (strong) NSMenuItem *menuItem1; // show application
I'll also add a property as an example.
The following code is for AppDelegate.m
#implementation AppDelegate
#synthesize menuItem1;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self setMainStatus];
}
- (void)setMainStatus {
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
statusImage = [NSImage imageNamed:#"statusImage"];
[statusItem setImage:statusImage];
[statusItem setMenu:statusMenu];
NSMutableString *menuname1 = [[NSMutableString alloc] initWithString:NSLocalizedString(#"statusMenuShowApplication", #"Show Application Window")];
menuItem1 = [[NSMenuItem alloc] initWithTitle:menuname1 action:#selector(statusApplicatinClicked:) keyEquivalent:#""];
[statusMenu addItem:menuItem1];
}
- (void)statusApplicatinClicked:(id)sender {
[self.window setIsVisible:YES];
}
#end
(3) Go back to Interface Builder and connect Status Menu to the NSMenu control you've added.
The setMainStatus method (or whatever you want to name) adds menu items to the status menu programatically. First, you need to create NSStatusbar, which takes NSImage. This NSImage is used to show an icon on the status menu. Next, add menu items and separators to the status menu (status Menu in my case). I have menuItem1 as a property so that the application could enable/disable it. That's just a quick example. You can add NSView to the status menu. If you want to add a separator, it can go as follows.
[statusMenu addItem:[NSMenuItem separatorItem]];
You don't need an application window to run a status menu application. You have to show the main application window for the first time if you are going to submit your status application to Apple's Mac App Store, though.

The status bar is the top-right part of your entire screen. It's where the date and time, Spotlight, etc live in the Menu Bar.
The screenshot you posted is of the title bar of your primary NSWindow.

Related

cocoa-How to set status bar app left click event?

I'm new to objective-c programming and I'm trying to make a status bar application right now.
I only know how to set a dropdown menu to show when click on the status bar item.
However, what I want is to show a panel when left clicked and show the menu when right clicked, just like the way Bartender 2 acts.
I've referred this demo but I could hardly figure out what it does.
I use xib to build my UI. I have three .xib files: MainMenu, Preferences and MainPanel.
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property NSStatusItem *statusItem;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "Menu.h" //Menu is a ViewController for Menu.xib
#interface AppDelegate ()
//#property (weak) IBOutlet NSWindow *window; //I don't know what is this for
#end
#implementation AppDelegate
#synthesize statusItem;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
//I initiate my statusItem here
-(void)awakeFromNib{
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
self.statusItem.title = #"T";
// you can also set an image
//self.statusBar.image =
self.statusItem.highlightMode = YES;
//I tried to use these code to set the left click action
[statusItem setTarget:self];
[statusItem setAction:#selector(showMenu:)];
}
-(void)showMenu{
Menu* menuVC = [[Menu alloc] initWithNibName:#"Menu" bundle:nil];
//Don't know what to do next...
}
#end
I tried to use
[menuVC showWindow];
but it is not right.
See this post for information about drawing an NSMenu at a given NSPoint (use popUpContextMenu:withEvent:forView: instead of showWindow).
I'll also point out that in the example you linked, the author modularizes his project into different components, following the MVC pattern. There is a component for the menu controller and view (it doesn't look like he just displays an NSMenu), as well as the panel controller and view. You may want to think about how you can organize your project to follow the conventions of MVC.

How to update a menu item in a status menu on Objective-C

I'm a very beginner in Objective-C and in Mac development. I've searched this site for the answer I'm seeking, but due to a big ignorance I could not understand how this should be done.
The following piece of code implements a menu in Mac status bar, near the battery, wifi, etc icons on the top right of the screen.
If the user disconnects a device from the computer the method sayAdapterIsConnected is called with one boolean argument. Depending on that argument, one of the menu items, in this case the enabledOption, should get disabled. I mean, the menu item should be enabled or disabled.
This works only if the menu is closed. If the statusMenu is opened, it is not updated.
I know there is a way to update an open statusItem menu. Wifi status icon updates its networks while the menu is open.
#interface ENHeringAppDelegate : NSObject <NSApplicationDelegate> {
NSStatusItem * statusItem;
}
#property (weak) IBOutlet NSMenu *statusMenu;
#property (weak) IBOutlet NSMenuItem *enabledOption;
#end
#implementation ENHeringAppDelegate
- (void) awakeFromNib {
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem setMenu:_statusMenu];
[statusItem setTitle:#"TEST"];
[statusItem setHighlightMode:YES];
}
- (void)sayAdapterIsConnected:(bool)connected {
if (connected)
[self.enabledOption setEnabled:true];
else
[self.enabledOption setEnabled:false];
}
#end
A NSMenu isn't needed here. You also don't need the setMenu: call.
Here's a working example:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface KBCAppDelegate : NSObject <NSApplicationDelegate> {
NSStatusItem *statusItem;
}
#property (assign) IBOutlet NSWindow *window;
- (IBAction)update:(id)sender;
#end
AppDelegate.m
#import "AppDelegate.h"
#implementation KBCAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem setTitle:#"TEST"];
[statusItem setHighlightMode:YES];
[statusItem setEnabled:YES];
[statusItem setTarget:self];
}
- (IBAction)update:(id)sender {
if (!statusItem.isEnabled) {
[statusItem setEnabled:true];
} else {
[statusItem setEnabled:false];
}
}
#end
That's all you need to do. (I made update: an IBAction, but a void function like what you show would work too)
P.S.
Make us nitpickers happy and always use braces in your if/then statements, even if there is only one line. :)

How to change NSMenuItem Title (Login to Logout)

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 IBOutlets 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
You don't need to connect your menu item just try this..
NSMenuItem *menuItem = (NSMenuItem*) sender;
NSString *menuString = menuItem.title;
if ([menuString isEqualToString:#"Login"])
{
[menuItem setTitle:#"LogOut"];
}
NSMenuItem menuItem = (NSMenuItem) sender;
this line automatically collect the menu items in your app.

mouseDown issue on custom view for a menubar app

i'm trying to code a drag&drop menubar app. i used a custom view to access to the dropped file, and this works fine. now i would like to open the default menu when clicking on this view. i'm using this:
- (void)mouseDown:(NSEvent *)event {
[statusItem popUpStatusItemMenu:statusMenu];
}
now, the mouseDown works fine (trying with NSLog), but still i cannot access to statusItem and statusMenu.
this is in dropView.m, in dropView.h i got:
#interface dropView : NSView{
IBOutlet NSMenu *statusMenu;
NSStatusItem *statusItem;
}
no crash, no logsā€¦ any ideas? ty!
this is a bit more from the .m
- (void)awakeFromNib{
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem setMenu:statusMenu];
dropView *drop = [[dropView alloc] initWithFrame:NSMakeRect(0, 0, 24, 24)];
[statusItem setView:drop];
}
Add this to your .h file:
#property (strong, nonatomic) IBOutlet NSMenu *statusMenu;
#property (strong, nonatomic) NSStatusItem *statusItem;
Add this to your .m file:
#synthesize statusMenu, statusItem;
Then you'll access the properties with self.statusMenu and self.statusItem.

NSStatusItem appears briefly on launch, but promptly disappears

I'm starting to get back into Cocoa development after not working on anything for a few months. Originally when I started I was using Snow Leopard and Xcode 3. I'm now running Lion with Xcode 4.2 and I'm running into some issues that I hadn't run into before.
I believe it's probably the fact that I've never used ARC before, so I'm sure I'm missing something.
I'm trying to create Statusbar application, without a main window, or dock icon. When I run the application the Statusbar icon for my application appears briefly, for about a second, but then disappears.
Heres my code.
QuickPlusAppDelegate.h
#import <Cocoa/Cocoa.h>
#interface QuickPlusAppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (assign) NSStatusItem *statusItem;
#property (weak) IBOutlet NSMenu *statusItemMenu;
#property (strong) NSImage *statusItemIcon;
#property (strong) NSImage *statusItemIconHighlighted;
#property (strong) NSImage *statusItemIconNewNotification;
#end
QuickPlusAppDelegate.m
#import "QuickPlusAppDelegate.h"
#implementation QuickPlusAppDelegate
#synthesize statusItemMenu = _statusItemMenu;
#synthesize window = _window, statusItem = _statusItem;
#synthesize statusItemIcon = _statusItemIcon,
statusItemIconHighlighted = _statusItemIconHighlighted,
statusItemIconNewNotification = _statusItemIconNewNotification;
- (void) awakeFromNib
{
NSBundle *appBundle = [NSBundle mainBundle];
_statusItemIcon = [[NSImage alloc] initWithContentsOfFile:[appBundle pathForResource:#"statusItemIcon" ofType:#"png"]];
_statusItemIconHighlighted = [[NSImage alloc] initWithContentsOfFile:[appBundle pathForResource:#"statusItemIconHighlighted" ofType:#"png"]];
_statusItemIconNewNotification = [[NSImage alloc] initWithContentsOfFile:[appBundle pathForResource:#"statusItemIconNewNotification" ofType:#"png"]];
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[_statusItem setImage:_statusItemIcon];
[_statusItem setAlternateImage:_statusItemIconHighlighted];
[_statusItem setHighlightMode:YES];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// empty
}
#end
Edit If you see anything wrong with my code please let me know. I definitely would some critique so I can get better.
Another Edit It seems as if the Statusbar icon disappears when the main window itself loads.
_statusItem will be autoreleased in this case.
_statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
This returns an autoreleased object. _statusItem is just an iVar. Not only that, but you declare the property as assign:
#property (assign) NSStatusItem *statusItem;
What you probably want to do here is make the property strong and then, instead of setting the ivar directly, use the property to set it. So like this:
#property (strong) NSStatusItem *statusItem;
and then:
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
That will cause the statusItem be retained. I'm betting that what's happening now is that it gets released when the autorelease pool pops, and then your app crashes the next time anything tries to access it, thus causing it to disappear from the menu bar. Running it through the Zombies instrument would tell you for sure if that was what was happening. But in general, your app needs to have a strong reference to that object for it to stick around.
I was having this issue in Xamarin. For awhile it worked fine. Then I added extra code to the FinishedLaunching method and the StatusItem started vanishing. I had this code generating the StatusItem:
public override void AwakeFromNib ()
{
var statusItem = NSStatusBar.SystemStatusBar.CreateStatusItem (30);
statusItem.Menu = mainMenu;
statusItem.Image = NSImage.ImageNamed ("menuicon");
statusItem.AlternateImage = NSImage.ImageNamed ("menuicon_selected");
statusItem.HighlightMode = true;
}
Eventually, I found my problem. In my Xcode I had declared this property in my AppDelegate but I wasn't using it:
#property(nonatomic, retain) IBOutlet NSStatusItem *statusItem;
When I removed the var the StatusItem continued to show in its infinite glory :)
public override void AwakeFromNib ()
{
statusItem = NSStatusBar.SystemStatusBar.CreateStatusItem (30);
statusItem.Menu = mainMenu;
statusItem.Image = NSImage.ImageNamed ("menuicon");
statusItem.AlternateImage = NSImage.ImageNamed ("menuicon_selected");
statusItem.HighlightMode = true;
}
I didn't have to change it to (strong). In fact I tried but it didn't persist when copying back to Xamarin Studio.