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

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.

Related

Changing views on a window with a button click

what I'm basically trying to make is a very simple program that can switch back and forth between 2 views on a single window.
When the program loads there is a window with a custom view that contains a login button. When clicked, the view changes to a second custom view that contains a label and a logout button. I have this much working.
What I can't figure out is how to get the logout button to bring me back to the first view.
Here is the code for the AppDelegate class where i have the button method to switch the view:
header:
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSView *loginView;
- (IBAction)loginButtonClicked:(id)sender;
#end
implementation:
#import "AppDelegate.h"
#import "myCustomView.h"
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (IBAction)loginButtonClicked:(id)sender {
[_loginView removeFromSuperview];
myCustomView *new = [[myCustomView alloc]initWithNibName:#"myCustomView" bundle:nil];
[[[self window]contentView]addSubview:[new view]];
}
#end
This is the code for my custom class that is a subclass of NSViewController.
header:
#import <Cocoa/Cocoa.h>
#class AppDelegate;
#interface myCustomView : NSViewController
#property (strong) IBOutlet NSView *logoutView;
- (IBAction)logoutButtonClicked:(id)sender;
#end
implementation:
#import "myCustomView.h"
#implementation myCustomView
- (void)viewDidLoad {
[super viewDidLoad];
// Do view setup here.
}
- (IBAction)logoutButtonClicked:(id)sender {
[_logoutView removeFromSuperview];
myCustomView *newController = [[myCustomView alloc]initWithNibName:#"MainMenu" bundle:nil];
[[[self window]contentView]addSubView:[newController view]];
//this does not work. No visible #interface for 'myCustomClass' declares the selector 'window'
}
#end
The button method to go back to the login page is where I'm stuck. Even though I've added the header file for AppDelegate into myCustomClass I cannot use the instance of NSWindow. What am I doing wrong here? Am I at least on the right track? any help here is greatly appreciated.
I also tried using #class instead of #import, but still can't use the instance of NSWindow from AppDelegate.
Here are the pictures of my two xib files:
[][1
UPDATE: The suggestions from Paul Patterson in his comments were very helpful, but haven't solved my problem. For now what I am doing to get my project to work is putting the buttons in the window instead of the views and then hiding them when i don't need them. This works and I can switch back and forth, however I still can't figure out how to use a button on a custom view itself to load a different view onto the same window.

Why are there two instances of the ViewController and how to fix it?

First I'll give you a short overview.
I'm ...
creating a new cocoa project
customizing the AppDelegate (see listing 1)
adding a "Custom View" to my MainMenu.xib
creating a new Cocoa Class (NSViewController + XIB) in the project, calling it MyTableViewController.*
adding a "Table View" to the recently added ViewController, like described in LINK
the code of my MyTableViewController can be seen in listing 2
Now to my problem.
The table and it's content is shown.
But if I select an item of the table and press a button (connected to (IBAction)action:(id)sender) on this subview, <NSIndexSet: 0x60000022c100>(no indexes) is shown in the output (see: selectedColumnIndexes).
After experimenting a while, I found out that there are two instances of the MyTableViewController class.
Can someone please explain me why there are two instances and help me to fix this problem.
Thx
listing 1:
// FILE: AppDelegate.h
#import <Cocoa/Cocoa.h>
#class MyTableViewController;
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (nonatomic, assign) NSViewController * currentViewController;
#property (nonatomic, strong) MyTableViewController * myTableViewController;
#property (weak) IBOutlet NSView *myview;
#end
// FILE: AppDelegate.m
#import "AppDelegate.h"
#import "MyTableViewController.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self changeViewController];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication {
return YES;
}
- (void)changeViewController
{
if ([self.currentViewController view] != nil) {
[[self.currentViewController view] removeFromSuperview];
}
switch (0) {
case 0:
default:
if (self.myTableViewController == nil) {
_myTableViewController = [[MyTableViewController alloc] initWithNibName:#"MyTableViewController" bundle:nil];
}
self.currentViewController = self.myTableViewController;
NSLog(#"EndView");
break;
}
[self.myview addSubview:[self.currentViewController view]];
[[self.currentViewController view] setFrame:[self.myview bounds]];
[self.currentViewController setRepresentedObject:[NSNumber numberWithUnsignedInteger:[[[self.currentViewController view] subviews] count]]];
[self didChangeValueForKey:#"viewController"];
NSLog(#"ViewController changed");
}
#end
listing 2:
// FILE: MyTableViewController.h
#import <Cocoa/Cocoa.h>
#interface MyTableViewController : NSViewController
#property (weak) IBOutlet NSTableView *tview;
- (IBAction)action:(id)sender;
#end
// FILE: MyTableViewController.m
#import "MyTableViewController.h"
#import "AppDelegate.h"
#interface MyTableViewController ()
#end
#implementation MyTableViewController
- (NSUInteger)numberOfRowsInTableView:(NSTableView *)tableViewObj {
return 2;
}
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
if ([tableView tableColumns][0] == tableColumn) {
return #"bla";
} else if ([tableView tableColumns][1] == tableColumn) {
return #"blub";
}
NSLog(#"dropped through tableColumn identifiers");
return NULL;
}
- (IBAction)action:(id)sender {
// selectedColumnIndexes
NSLog(#"%#", [self.tview selectedColumnIndexes]);
}
#end
Two instances of a class, when you only expect/want one, can be caused by failing to appreciate that objects in your xib files are themselves actual automatically-generated instances of that class.
Have you dragged a blue cube into either of your xib files and set it's class to your view controller subclass? If you have, then this will account for one of the objects that you're seeing - Apple's machinery creates it on your behalf. The second object is the one created by you, in code, in your changeViewController method.
I get the impression that you're simply trying to create a window, which contains an NSTableView, which in turn gets its data from your own NSViewController subclass. Is this correct? If it is then you should do away with the second xib file, and instead just use the xib created for you when you created the project.
In Brief
Drag a blue object cube into the Interface Builder dock and set it's subclass to MyTableViewController using the Identity Inspector.
With your view controller blue-cube selected go to the Connections Inspector and drag from the view option to your table view - you must make sure you're dragging to the table view, not the scroll view or clip view that encloses it.
Select the table view (again, make sure it really is the table view you've selected), and go to it's Connections Inspector. Drag from the data source and delegate options to your blue view-controller cube.
Implement the relevant data-soruce methods
Hint: If you aren't sure which inspector is which, open the right sidebar in Xcode and select a xib file from the left file-viewer sidebar. Sit the cursor over each of the icons at the top of the right sidebar and the tool tip will tell you which is which.
Update
A good way of identifying specific objects, and something that can assist with debugging, is to set their identifier. In the attributes inspector, this is the restoration ID. Do this for your NSTableView instance, and for your NSTableColumn instances. Then, in your data-source methods do some logging with them - for instance does the table view passed as the first argument for this methods have the expected identifier, what about the table columns?

mac status bar application not working

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.

Declaring objects from one xib to another in Xcode?

I have a project that has a switch from an xib separate from the main ViewController. I am trying to make it so that when you switch the UISwitch to OFF a button in the ViewController is hidden. I have tried to declare the AppDelegate in the xib's .m file but still no luck. My code is:
- (void)viewDidAppear:(BOOL)animated {
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
}
What am I doing wrong? I am trying to do
if (switch.on) {
myButton.hidden = YES;
} else {
myButton.hidden = NO;
}
I have also done this in the .m file of the second xib (the one that does not have the main ViewController)
#import "AppDelegate.h"
#import "ViewController.h"
But still NOTHING! So basically, I'm just trying to declare a button in one xib from another. THAT'S IT. PLEASE HELP Thanks!
First, see this about passing data between view controllers and this about UISwitch.
Once you understand that, set up your code something like this--
FirstViewController.h:
#interface FirstViewController : UIViewController
{
...
}
#property IBOutlet UISwitch *theSwitch;
- (IBAction)switchToggled;
#end
SecondViewController.h:
#interface SecondViewController : UIViewController
{
...
}
#property IBOutlet UIButton *button;
#property UIViewController *theFirstViewController;
#end
Be sure that theFirstViewController gets set somehow -- again, see passing data between view controllers. Then in switchToggled you can do this:
- (IBAction)switchToggled
{
theFirstViewController.button.hidden = YES;
}
The important thing to remember is that your view controllers don't magically know about each other. Even if you do something with the AppDelegate like you were trying. SecondViewController needs a reference to FirstViewController.

Connect NSImageView to view using IB?

I've only programmed on the iPhone so far, so Cocoa is sort of confusing in certain ways for me. Here's where I've hit a snag. I wanted my window so that the background was invisible, and without a title-bar. Something like this:
Here's how I'm doing it:
I set my window's class to a custom window, which I've created like this:
CustomWindow.h
#import <Foundation/Foundation.h>
#import <Cocoa/Cocoa.h>
#interface CustomWindow : NSWindow {
#private
NSPoint initialLocation;
}
#property(assign)NSPoint initialLocation;
#end
CustomWindow.m
//trimmed to show important part
#import "CustomWindow.h"
#implementation CustomWindow
#synthesize initialLocation;
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
// Removes the window title bar
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO];
if (self != nil) {
[self setAlphaValue:1.0];
[self setOpaque:NO];
}
return self;
}
#end
Now, in my .xib file for this window I've added a custom view onto the window. I've set the view class to a custom class I've created that inherits from NSView. Here's how I'm setting that up:
MainView.h
#import <Cocoa/Cocoa.h>
#interface MainView : NSView {
#private
//nothing to see here, add later
}
#end
MainView.m
//trimmed greatly again to show important part
#import "MainView.h"
#implementation MainView
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)drawRect:(NSRect)rect {
// Clear the drawing rect.
[[NSColor clearColor] set];
NSRectFill([self frame]);
}
#end
So here's my question. I've added a NSImageView to my custom view (MainView) in Interface Builder. However, for some reason I can't figure out how to connect this image view to an instance variable in my custom view. They seem like they can't be connected like I normally would if I was creating an iPhone app. Any ideas how this would be done?
You connect objects created in your XIB in Mac OS X the same way you do for iOS programs. Just add an NSImageView property to your main view, mark it as an IBOutlet and connect it up.
For example,
In MainView.h create a property for your NSImageView and make it an IBOutlet:
#import <Cocoa/Cocoa.h>
#interface MainView : NSView {
NSImageView *imageView;
}
#property(retain) IBOutlet NSImageView *imageView;
#end
In interface builder, make sure the class for the custom view is set to MainView, to do this click on the File's Owner object in the custom view XIB and then select the identity option in the inspector and enter MainView as the class type.
Next, CTRL+click File's owner and drag the arrow to the NSImageView and select the imageView outlet.
That's all there is to it. You should be able to reference the image view from code now.