How to deal with a Toggle NSButton? - cocoa

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];

Related

Menu items don't appear in nibless StatusMenu app?

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.

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

Mac OS X Cocoa multiview application navigation

I've already spent 2 full days trying to figure out how to use NSViewControllers in order to create a multiview application.
Here is what I do.
I have 2 View Controllers and the MainMenu.xib's Window.
I also have an AppController that is the delegate for both View Controllers.
When I launch the app, I'm first greeted with the MainMenu.xib's Window's view which holds a button. On clicking this button, an IBAction is sent to the appController and asks for the SecondViewController to display it's nib. So far, everything's fine and the nib file is displayed correctly.
On the secondViewController, there's another button that sends another IBAction to the appController and asks for the FirstViewController to be displayed but nothing happens,
no crash, no warning... Any help would be much appreciated...
Thanks in advance for your patience...
Here is the code for the AppController.h :
#import <Foundation/Foundation.h>
#import "SecondViewController.h"
#import "FirstViewController.h"
#interface AppController : NSObject
#property (strong) IBOutlet NSWindow *mainWindow;
#property (strong) IBOutlet SecondViewController *secondViewController;
#property (strong) IBOutlet FirstViewController *firstViewController;
- (IBAction)secondButtonfromsecondViewControllerClicked:(id)sender;
- (IBAction)buttonClicked:(id)sender;
#end
and here is the code for the AppController.m :
#import "AppController.h"
#implementation AppController
#synthesize mainWindow = mainwindow;
#synthesize secondViewController;
#synthesize firstViewController;
- (IBAction)buttonClicked:(id)sender {
NSLog(#"button from second View Controller clicked");
self.secondViewController = [[SecondViewController
alloc]initWithNibName:#"SecondViewController" bundle:nil];
self.mainWindow.contentView = self.secondViewController.view;
[self.secondViewController.view setAutoresizingMask:NSViewWidthSizable |
NSViewHeightSizable];
}
- (IBAction)secondButtonfromsecondViewControllerClicked:(id)sender {
NSLog(#"button from first ViewController clicked");
self.firstViewController = [[FirstViewController
alloc]initWithNibName:#"FirstViewController" bundle:nil];
self.mainWindow.contentView = [self.firstViewController view];
}
#end
Well, anyone can help me, I just need a single view application that displays a first ViewController with a button on the first viewController that takes me to a second view controller with a second button that takes me back to my first viewcontroller... I've already spent more than a week on that... in vain... PS : I don't want any button on the mainMenu.xib window nor tabs.
here is the solution to my question then.
Here's the code for the AppDelegate.h:
// AppDelegate.h
#import <Cocoa/Cocoa.h>
#import "FirstViewController.h"
#import "SecondViewController.h"
//We need to declare the AppDelegate class as being the delegate for both
//FirstViewController and SecondViewController
#interface AppDelegate : NSObject <NSApplicationDelegate,
FirstViewControllerDelegate, SecondViewControllerDelegate>
#property (strong, nonatomic) NSWindow *window;
#property (strong) FirstViewController *firstViewController;
#property (strong) SecondViewController *secondViewController;
-(void) goToSecondView;
-(void) goToFirstView;
#end
Now, here's the AppDelegate.m:
// AppDelegate.m
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
#synthesize firstViewController;
#synthesize secondViewController;
-(void) awakeFromNib {
[self goToFirstView];
self.firstViewController.delegate = self;
}
-(void) goToSecondView {
if (self.secondViewController ==nil) {
self.secondViewController =[[SecondViewController alloc]
initWithNibName:#"SecondViewController" bundle:nil];
}
self.window.contentView = [self.secondViewController view];
}
-(void) goToFirstView {
if (self.firstViewController ==nil) {
self.firstViewController =[[FirstViewController alloc]
initWithNibName:#"FirstViewController" bundle:nil];
}
self.window.contentView = [self.firstViewController view];
}
#end
Next we need to set delegates in the FirstViewController and the SecondViewController
// FirstViewController.h
#import <Cocoa/Cocoa.h>
#import "SecondViewController.h"
//We declare the delegation protocole:
#protocol FirstViewControllerDelegate <NSObject>
-(void)goToSecondView;
#end
#interface FirstViewController : NSViewController
- (IBAction)firstViewControllerButtonClicked:(id)sender;
#property (nonatomic, strong) id <FirstViewControllerDelegate> delegate;
#end
And here is the FirstViewController.m:
// FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = [NSApp delegate];
}
return self;
}
- (IBAction)firstViewControllerButtonClicked:(id)sender {
NSLog(#"button from first View Controller clicked");
if ([self.delegate respondsToSelector:#selector(goToSecondView)]) {
[self.delegate goToSecondView];
}
}
#end
Now, same thing for the SecondViewController:
// SecondViewController.h
#import <Cocoa/Cocoa.h>
#protocol SecondViewControllerDelegate <NSObject>
-(void)goToFirstView;
#end
#interface SecondViewController : NSViewController
#property (nonatomic, strong) id <SecondViewControllerDelegate> delegate;
- (IBAction)goToFirstViewControllerButtonClicked:(id)sender;
#end
And here's the SecondViewController.m:
// SecondViewController.m
#import "SecondViewController.h"
#interface SecondViewController ()
#end
#implementation SecondViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = [NSApp delegate];
}
return self;
}
- (IBAction)goToFirstViewControllerButtonClicked:(id)sender {
NSLog(#"button from Second View Controller clicked");
if ([self.delegate respondsToSelector:#selector(goToFirstView)]) {
[self.delegate goToFirstView];
}
}
#end
Well, I guess this code may be improved and if you have any suggestion, feel free to let me know. Hope it will help others.
THE PROBLEM: When the user presses a button in View2, you want View1 to appear. It's not.
STEP 1: You say that the button should be invoking an action on your AppController. Set a breakpoint (or add a diagnostic log) in that action, just to verify that it is, in fact, being invoked.
STEP 2: Think about what you want that action to do, precisely. My guess is that you want to hide View2 and show View1. Perhaps
[view2 setHidden: YES];
[view1 setHidden: NO];
(I'm not using your names here, of course.) Or you might animate the transitions, either cross-fading the views or moving them.
STEP 3: My guess is that STEP 2 will solve your problem. If it doesn't, use the debugger again to verify that view1 and view2 are not null. (If they're null, you probably have weak variables where you need them to be strong.)
STEP 4: In the unlikely event that you're still stuck, check the frames of view1 and view2. Perhaps view1 isn't where you think it is.
STEP 5: If you're still stuck, check the alphaValue of view1. Maybe you set it to be transparent, and it's being drawn transparently in the right place.
STEP 6: I bet there is no step 6!
This isn't much of an answer at the moment, however I have some concerns about your code that I wanted to work through with you.
Are you sure you have connected the outlets and actions in Interface Builder. Please verify this.
You don't need mainWindow as there is already a window property that points to the main window (verify this in Interface Builder). Also this looks wrong:
#synthesize mainWindow = mainwindow;
^
W
So dump that and just use the existing window outlet provided by Xcode.
Don't re-create the view controllers if they already exist:
if (self.secondViewController == nil)
{
self.secondViewController = [[SecondViewController alloc] initWithNibName:#"SecondViewController"
bundle:nil];
}
self.window.contentView = self.secondViewController.view;

MapKit pin title not showing

The location is working but the title isn't appearing, most strange.
location.latitude = (double) 51.501468;
location.longitude = (double) -0.141596;
// Add the annotation to our map view
MapViewAnnotation *newAnnotation = [[MapViewAnnotation alloc] initWithTitle:#"ABC" andCoordinate:location];
[self.mapView addAnnotation:newAnnotation];
// [newAnnotation release];
MapViewAnnotation.h
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface MapViewAnnotation : NSObject <MKAnnotation> {
NSString *title;
CLLocationCoordinate2D coordinate;
}
#property (nonatomic, copy) NSString *title;
#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d;
#end
and MapViewAnnotation.m
#import "MapViewAnnotation.h"
#implementation MapViewAnnotation
#synthesize title, coordinate;
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d {
title = ttl;
coordinate = c2d;
return self;
}
#end
[newAnnotation release] is remmed out to keep the ARC happy :-)
Any ideas?
This did the trick:
[mapView selectAnnotation:newAnnotation animated:YES];
previously the title would only show if you clicked on the Pin.
you call annotation delegates refer this link, mapkit-example-in-iphone
That code looks fine (except for the non-standard implementation of the init method).
The most likely reason the title (callout) isn't appearing is that in your viewForAnnotation delegate method, you are not setting canShowCallout to YES (it defaults to NO).
In the viewForAnnotation delegate method, after you create the MKAnnotationView, set canShowCallout to YES.
Unrelated to the title/callout not showing, the init method in your MapViewAnnotation class should be implemented like this:
- (id)initWithTitle:(NSString *)ttl andCoordinate:(CLLocationCoordinate2D)c2d {
self = [super init];
if (self) {
title = ttl;
coordinate = c2d;
}
return self;
}

Switching between two NSWindows

I am making a simple mac app in which i want to switch windows.
I have two NSWindowController class MainWindow and DetailWindow
I am using this code :
MainWindow class:
//MainWindow.h
#class DetailWindow;
#interface MainWindow : NSWindowController{
IBOutlet NSButton *btn1;
DetailWindow *detailwindow;
}
#property (nonatomic, retain) IBOutlet NSButton *btn1;
- (IBAction)btn1Event:(id)sender;
//MainWindow.m
#implementation MainWindow
#synthesize btn1;
- (IBAction)btn1Event:(id)sender {
if (!detailwindow) {
detailwindow = [[DetailWindow alloc] initWithWindowNibName:#"DetailWindow"];
}
[detailwindow showWindow:self];
}
#end
DetailWindow Class:
//DetailWindow.h
#class MainWindow;
#interface DetailWindow : NSWindowController{
IBOutlet NSButton *backbtn;
MainWindow *mainwindow;
}
#property (nonatomic, retain) IBOutlet NSButton *backbtn;
- (IBAction)back:(id)sender;
//DetailWindow.m
#implementation DetailWindow
#synthesize backbtn;
- (IBAction)back:(id)sender {
if (!mainwindow) {
mainwindow = [[MainWindow alloc] initWithWindowNibName:#"MainWindow"];
}
[mainwindow showWindow:self];
}
#end
Now the problem is when i click backbtn on DetaiWindow it will open a new MainWindow.
So i have two MainWindow on screen.
I want just main window at front when i click backbtn.
Any help??
Thank you..!!
Your basic problem is that each window is assuming that it is its own job to create the other. Each has an ivar for the other, but there's no external access to it -- via a property or being an IBOutlet or anything else -- so it always starts out as nil, and a new copy gets created instead of reusing the old one.
There are any number of ways to get around this. Probably the easiest would be to create both windows in Interface Builder and link them up there, having made the ivars IBOutlet. Then you know you never have to create them in code at all.
However, purely on the basis of inertia, here's an alternative that sticks closer to what you've got already. Note that I've assumed for simplicity that mainWindow always exists first. If not, you'll have to duplicate the process the other way around.
//MainWindow.h
#class DetailWindow;
#interface MainWindow : NSWindowController
{
IBOutlet NSButton *btn1;
DetailWindow *detailwindow;
}
#property (nonatomic, retain) NSButton *btn1;
- (IBAction)btn1Event:(id)sender;
//MainWindow.m
#implementation MainWindow
#synthesize btn1;
- (IBAction)btn1Event:(id)sender
{
if (!detailwindow)
{
detailwindow = [[DetailWindow alloc] initWithWindowNibName:#"DetailWindow"];
// having created the other window, give it a reference back to this one
detailWindow.mainWindow = self;
}
[detailwindow showWindow:self];
}
#end
//DetailWindow.h
#class MainWindow;
#interface DetailWindow : NSWindowController
{
IBOutlet NSButton *backbtn;
MainWindow *mainwindow;
}
#property (nonatomic, retain) NSButton *backbtn;
// allow the main window to be set from outside
#property (nonatomic, retain) MainWindow *mainWindow;
- (IBAction)back:(id)sender;
//DetailWindow.m
#implementation DetailWindow
#synthesize backbtn;
#synthesize mainWindow;
- (IBAction)back:(id)sender
{
// no window creation on the way back
NSAssert(mainWindow, "mainWindow not set!");
[mainwindow showWindow:self];
}
#end
Untested, so usual caveats apply.
You have to call orderFront: method with self object on main window.
To do this you must find a reference to the main window. A way to do this is:
[NSApp mainWindow];
This call will return you a pointer to your main window (If you did something incorrect, you could have to cycle through the [NSApp windows] array in order to search for your main window).
When you have found the window, send it a orderFront message, by doing (supposing the code above returns the correct window, as explained before).
[[NSApp mainWindow] orderFront:self];
and the window should magically order front.

Resources