Menu items don't appear in nibless StatusMenu app? - xcode

I've been working on an application port that must show a status menu and some dynamic items within. It's behaviour is similar to the apple WIFI menu which has an icon, some fixed items and some dynamic items in the center (the available WIFI networks).
For various reasons I decided to go nibless. I've managed to get the menu icon to appear, but I can't seem to get the items to show in the menu when I click the icon.
This is what I have so far:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
// Strange, if these are not properties and not declared strong,
// the menu flashes momentarily and disappears. Could the dynamic menu items be related to object lifetime?
#property (strong, nonatomic) IBOutlet NSMenu *statusMenu;
#property (strong, nonatomic) NSStatusItem *statusItem;
#end
AppDelegate.m
import "AppDelegate.h"
#implementation AppDelegate
- (IBAction)loginClicked:(id)sender
{
NSLog(#"LoginClicked");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSLog(#"AppDidFinishLaunching!");
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[self.statusItem setMenu:self.statusMenu];
NSImage *statusImage = [[NSImage alloc] initWithContentsOfFile:#"/tmp/applogo.png"];
//menuImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:#"applogo" ofType:#"png"]];
[self.statusItem setImage:statusImage];
[self.statusItem setAlternateImage:statusImage];
//[self.statusItem setTitle:#"MyApp"];
[self.statusItem setHighlightMode:YES];
// Add login
NSMenuItem *login = [[NSMenuItem alloc] initWithTitle:#"Login" action:loginClicked keyEquivalent:#""];
[self.statusMenu addItem:login];
NSMenuItem *quit = [[NSMenuItem alloc] initWithTitle:#"Quit" action:nil keyEquivalent:#""];
[self.statusMenu addItem:quit];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
main.m
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, const char* argv[])
{
#autoreleasepool { // Do I need this or is it on by default?
// make sure the application singleton has been instantiated
NSApplication * application = [NSApplication sharedApplication];
// instantiate our application delegate
AppDelegate * applicationDelegate = [[AppDelegate alloc] init];
// assign our delegate to the NSApplication
[application setDelegate:applicationDelegate];
// call the run method of our application
[application run];
}
// execution never gets here...
return 0;
}

After much mucking around, I tried something that now seems obvious.
I don't know why, but in examples of StatusBar Apps that use nib, IBMenu is not alloc'ed and inited. It appears to be done automatically somehow.
Adding the following seemed to fix it.
self.statusMenu = [[NSMenu alloc] init];
Also, noting my comment on lifetime. If I don't use properties, I have to set retain on statusItem when creating it. Then it doesn't get destroyed. I'm still learning about how objective-C managages object lifetime and still a bit confused about ARC here. But it least it now seems to show my menu items.

Related

Black sceen trying to put a .Xib after Splash

I'm programming a App for iOS 8. I've a problem because i don't want use storyboard, I want to put a .xib file at the first time. Muy problem is that always, after splash be a black screen.
The Class who I want to put after Splash is LoginViewController
At general options, the "Main interface" is white (empty) (if put the name of the class, i've NSException (NSUknownException)).
In the .xib file i'have the files owner connected, and at the right side of the screen i've the name of the class at "Custom Class" (LoginViewController).
My appDelegate.h is: (I try with " #property (strong, nonatomic) LoginViewController *viewController;" too)
#import <UIKit/UIKit.h>
#import "LoginViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UIViewController *viewController;
#end
My appDelegate.m is: (i try a lot of variants)
#import "AppDelegate.h"
#import "LoginViewController.h"
#interface AppDelegate ()
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.viewController = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
navController.navigationBarHidden = YES;
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
#end
The LoginViewController.h:
#import <UIKit/UIKit.h>
#interface LoginViewController : UIViewController
{
IBOutlet UILabel *labelPrueba;
IBOutlet UIButton *botonPrueba;
IBOutlet UIButton *boton2prueba;
IBOutlet UILabel *label2Prueba;
IBOutlet UILabel *dsfd;
IBOutlet UIButton *dfdf;
}
#end
The LoginViewController.m:
#import "LoginViewController.h"
#interface LoginViewController ()
#end
#implementation LoginViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
label2Prueba.text = #"laaaaaaaaaaaaa";
NSLog(#"Entra en viewDidLoad");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
Someone can help me please. I'm going crazyyyyy.
Thanks a lot.
The solution was put this windows like init frame:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// That was the solution
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// That was the solution
self.viewController = [[LoginViewController alloc] initWithNibName:#"LoginViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
navController.navigationBarHidden = YES;
self.window.rootViewController = navController;
[self.window makeKeyAndVisible];
return YES;
}
Thanks

NSWindowController's window released immediately

I'm trying to open a window using a NSWindowController in my app delegate.
I created a basic NSWindowController with an associated NIB and try to show the window that way:
#implementation MyAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Show the main window from a separate nib
MyWindowController * theWindowController = [[MyWindowController alloc] initWithWindowNibName:#"MyWindowController"];
[theWindowController showWindow:self];
}
#end
When I launch the app, the window of MyWindowController only appears for a fraction of second (seems to be released as soon as it launches).
Using ARC, how could I force the window to stick around and not be flushed right away? I do not use NSDocuments and I want to be able to use many of these MyWindowController concurrently.
You need to add a property to your app delegate (or some other object that's going to stick around for the lifetime of your app) that retains theWindowConroller. For example:
#interface MyAppDelegate : NSObject
#property (strong, nonatomic) MyWindowController * windowController;
#end
Then set this property when you initialize the window controller.
#implementation MyAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Show the main window from a separate nib
self.windowController = [[MyWindowController alloc] initWithWindowNibName:#"MyWindowController"];
[theWindowController showWindow:self];
}
#end

Creating status item - icon shows up, menu doesn't

In a document-based project I am trying to create a status menu. I have a singleton class that builds the status bar, and I am initiating it from an application delegate, as you can see. When I run this, I get no errors, but only an image of the status bar, but no menu drops down. I created the menu in IB. What am I messing up?
Delegate
#import "KBAppDelegate.h"
#import "KBStatusMenu.h"
#implementation KBAppDelegate
#synthesize window = _window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
KBStatusMenu *aStatusItem = [[KBStatusMenu alloc] init];
aStatusItem = [[KBStatusMenu instance] buildStatusItem];
}
#end
.h
#import <Foundation/Foundation.h>
#interface KBStatusMenu : NSObject
{
NSStatusItem *myStatusItem;
NSImage *statusImage;
IBOutlet NSMenu *myStatusMenu;
}
+ (KBStatusMenu *)instance;
- (id)buildStatusItem;
#end
.m
#import "KBStatusMenu.h"
#implementation KBStatusMenu
static KBStatusMenu *gInstance = nil;
+ (KBStatusMenu *)instance
{
#synchronized(self) {
if (gInstance == nil)
gInstance = [[self alloc] init];
}
return(gInstance);
}
- (id)buildStatusItem
{
myStatusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
statusImage = [NSImage imageNamed:#"statusNormTemplate.png"];
[myStatusItem setImage:statusImage];
[myStatusItem setHighlightMode:YES];
[myStatusItem setMenu:myStatusMenu];
return myStatusItem;
}
#end
You declared myStatusMenu as an outlet, but never loaded a nib (or assigned anything to it yourself). An outlet cannot get objects out of nowhere; the outlet is set only when you load a nib that has the outlet connected to something (or assign something to the variable yourself, as if it weren't an outlet).
You can prove this by adding a line to buildStatusItem that logs the value of the myStatusMenu instance variable. I expect that it will be nil.
What you need to do is:
Create a nib to contain the status item's menu.
Set the class of the File's Owner to KBStatusMenu.
In KBStatusMenu, implement init to load the nib you just created.
Then, by the time you reach buildStatusItem, loading the nib will have set the outlet, and you will have a menu to give to your status item.
I would recommend only creating one KBStatusMenu instance. In this case, I recommend enforcing the singleton: init should test whether gInstance has already been set and, if so, return that; only if it hasn't should it initialize and return self.

How to deal with a Toggle NSButton?

My application contains a PLAY/PAUSE button that is set to type Toggle in Interface Builder. I use it - as the name reveals - to play back my assets or to pause them.
Further, I am listening to the SPACE key to enable the same functionality via the keyboard shortcut. Therefore, I use keyDown: from NSResponderin my application. This is done in another subview. The button itself is not visible at this time.
I store the current state of playback in a Singleton.
How would you update the title/alternative title for the toogle button while taking into account that its state could have been altered by the keyboard shortcut? Can I use bindings?
I managed to implement the continuous update of the button title as follows. I added a programmatic binding for the state (in the example buttonTitle). Notice, that the IBAction toggleButtonTitle: does not directly change the button title! Instead the updateButtonTitle method is responsible for this task. Since self.setButtonTitle is called the aforementioned binding gets updated immediately.
The following example shows what I tried to describe.
// BindThisAppDelegate.h
#import <Cocoa/Cocoa.h>
#interface BindThisAppDelegate : NSObject<NSApplicationDelegate> {
NSWindow* m_window;
NSButton* m_button;
NSString* m_buttonTitle;
NSUInteger m_hitCount;
}
#property (readwrite, assign) IBOutlet NSWindow* window;
#property (readwrite, assign) IBOutlet NSButton* button;
#property (readwrite, assign) NSString* buttonTitle;
- (IBAction)toggleButtonTitle:(id)sender;
#end
And the implementation file:
// BindThisAppDelegate.m
#import "BindThisAppDelegate.h"
#interface BindThisAppDelegate()
- (void)updateButtonTitle;
#end
#implementation BindThisAppDelegate
- (id)init {
self = [super init];
if (self) {
m_hitCount = 0;
[self updateButtonTitle];
}
return self;
}
#synthesize window = m_window;
#synthesize button = m_button;
#synthesize buttonTitle = m_buttonTitle;
- (void)applicationDidFinishLaunching:(NSNotification*)notification {
[self.button bind:#"title" toObject:self withKeyPath:#"buttonTitle" options:nil];
}
- (IBAction)toggleButtonTitle:(id)sender {
m_hitCount++;
[self updateButtonTitle];
}
- (void)updateButtonTitle {
self.buttonTitle = (m_hitCount % 2 == 0) ? #"Even" : #"Uneven";
}
#end
If you store your state in an enum or integer a custom NSValueTransformer will help you to translate a state into its button title equivalent. You can add the NSValueTransformer to the binding options.
NSDictionary* options = [NSDictionary dictionaryWithObject:[[CustomValueTransformer alloc] init] forKey:NSValueTransformerBindingOption];
[self.button bind:#"title" toObject:self withKeyPath:#"buttonTitle" options:options];

COCOA: NSStatusitem icon disappearing on launching external code

I am a .Net developer that needs to port a small program over to Mac OS X. I have this mostly done (partly thanks to people on this site, thanks!) but have a bug that maybe people I can get help with.
I am creating a tool that sits in the status bar, that when clicked opens a window with several links or buttons. When the links or buttons are clicked they either open a website or external program. The problem is that the icon in the status bar disappears as I launch one of these external commands. Even more interesting is that the space on the status bar where the icon should be still responds; meaning that if I click on the area (even without the visible icon) it still runs the code and opens the window.
Here is the current code:
tray.m
#import "tray.h"
#import "MyView.h"
#implementation Tray
-(void) awakeFromNib{
NSBundle *bundle = [NSBundle mainBundle];
statusItem = [[NSImage alloc] initWithContentsofFile:[bundle pathForResource:"#icon" ofType:#"png"]];
MyView *view = [MyView new];
[statusItem setImage:statusImage];
view.image = statusImage;
[statusitem setView:view];
[statusitem setToolTip:#"Tray App"];
[view setTarget:self];
[view setAction:#selector(openWindow)];
}
-(IBAction)openWindow:(id)sender{
[trayWin makeKeyAndOrderFront:nil];
}
-(IBAction)openActMon:(id)sender {
(void)system("open '\/Applications/Utilities/Activity Monitor.app'");
}
tray.h
#import "MyView.h"
#interface Tray : NSObject {
NSStatusItem *statusItem;
NSImage *statusImage;
IBOutlet NSWindow * trayWin;
IBOutlet NSButton *ActMon;
void *openWindow;
}
#property (retain,nonatomic) NSStatusItem *statusItem;
-(IBAction)ActMon:(id)sender;
#end
MyView.h
#interface MyView : NSControl {
NSImage *image;
id target;
SEL action;
}
#property (retain)NSImage *image;
#property (assign) id target;
#property (assign) SEL action;
#end
MyView.m
#import "MyView.h"
#implementation MyView;
#synthethize image, target, action;
-(void)mousemouseUP:(NSEvent *)event{
[NSApp sendAction:selfself.action to:self.target from:self];
}
-(void)dealloc {
self.image = nil;
[super dealloc];
}
-(void)drawRect:(NSRect)rect {
[self.image drawInRect:CGRectMake(0,0,18,18) fromRect:NSZeroRect operation:NSCompositeSourceOver];
}
#end
}
The openActMon is run when the image/button is clicked, the image is located in the trayWin Window that is opened when the icon is clicked. At this point, Activity monitor successfully launches, but the icon in the StatusBar disappears.
I have tried putting a [super setNeedsDisplay:YES] in the openActMon, but that didn't help. And I added [view setNeedsDisplay:YES] in the openActMon and it responded undeclared.
I have given all of this code because, as I said, I am not a Objective-C coder, but .Net who just needs to port something small over. Hoping that this will be helpful to others in the future. Alot of this I have hodgepodged together from different forums and sites or have gotten from some help on StackOverflow. I am hoping someone can help.
Thanks in advance!
In awakeFromNib add:
[statusitem retain];
In awakeFromNib, you are allocating the NSImage into statusItem. I think you mean to allocate it into statusImage.

Resources