Improper setup of window displayed through menu item? - objective-c

I am working on my first native mac app after playing with iOS for a while.
I am attempting to launch a window from a menu item, but I suspect I am doing it wrong. Any IBAction I connect to buttons on this new window returns an error.
Here is what I am doing. From a menu item I launch this:
-(IBAction)displaySAInput:(id)sender{
NSLog(#"Input selected.");
inputSAViewController = [[NSWindowController alloc] initWithWindowNibName:#"InputViewController"];
[inputSAViewController showWindow:self];
This launches the InputViewController nib that is owned by the InputViewController class. I set the InputViewController class to inherit from NSWindowController.
On InputViewController.m I have tested IBActions such as:
-(IBAction)testButton:(id)sender{
NSLog(#"Data recalled?");
}
I connect this IBAction to a button through the Interface Builder. All looks okay.
When I build and open the InputViewController window I receive this error in the console before clicking anything:
Could not connect the action testButton: to target of class NSWindowController
I have searched extensively but my ignorance prevents me from connecting the dots. This thread based on a similar error with NSApplication looks promising, but I don't quite understand what I'd need to make the connections happen related to the NSWindowController error.
This should be simple. What am I missing?

Your code:
-(IBAction)displaySAInput:(id)sender{
NSLog(#"Input selected.");
inputSAViewController = [[NSWindowController alloc]
initWithWindowNibName:#"InputViewController"];
[inputSAViewController showWindow:self];
}
Notice you are alloc/initing a generic instance of NSWindowController, not your custom subclass where you've implemented the testButton: method. I assume you'd likely want that changed to:
-(IBAction)displaySAInput:(id)sender{
NSLog(#"Input selected.");
inputSAViewController = [[InputViewController alloc]
initWithWindowNibName:#"InputViewController"];
[inputSAViewController showWindow:self];
}

Just load the NSWindow before hand and make it invisible and when the IBAction is called, just do the following:
[myWindow setIsVisible:YES];
[myWindow setLevel:NSFloatingWindowLevel];
Yay!

Related

How to open an NSWindowController from another NSWindowController in Cocoa

I'm developing an app in cocoa for MacOSX in Xcode5 and I want to open another window from my current window by pressing a button, this is my code:
- (IBAction)openWindow:(id)sender {
WindowController *controllerWindow = [[WindowController alloc] initWithWindowNibName:#"WindowController"];
[controllerWindow showWindow:nil];
[[controllerWindow window] makeMainWindow];
}
so far I can see the window appearing by one second and then this just dissappear, how to do this correctly???
Neither the window nor the window controller have a strong reference anywhere outside the scope of this method.
So after that, they're getting released.
Normally, you would add your window controller to some container like an array in your app delegate.
The array will retain the window controller.
The window controller can hang on to the window.
It also makes sense for the action method to be in the app delegate. You button should just send a selector up the responder chain.
use this..
Create a new .h & .m files which yo need to open, as NewWindowController (for eg.) along with its .xib
And on any button click, to open the newly defined window, just allocate, instantiate and present..
NewWindowController *controllerWindow = [[NewWindowController alloc] initWithWindowNibName:#"NewWindowController"];
[controllerWindow showWindow:self];

How to get Main Window (App Delegate) from other class (subclass of NSViewController)?

I'm trying to change my windows content, from other class , that is subclass of NSViewController.I'm trying code below, but it doesn't do anything.
[NSApplication sharedApplication]mainWindow]setContentView:[self view]]; //code in NSViewController
[NSApplication sharedApplication]mainWindow] // returns null
I tried to add
[window makeMainWindow];
in App Delegate class, but it won't help.
Did I miss something?
P.S. Also I'm using code below to call any delegate function in my class,
[(appDelegate *) [[NSApplication sharedApplication]delegate]MyMethod];
but I wonder is there something better, wihtout importing delegate class. Something like this
[[NSApplication sharedApplication]delegate]MyMethod];
(it gives warning)
For the mainWindow method the docs say:
This method might return nil if the application’s nib file hasn’t finished loading, if the receiver is not active, or if the application is hidden.
I just created a quick test application and I placed the following code:
NSLog(#"%#", [[NSApplication sharedApplication] mainWindow]);
into my applicationDidFinishLaunching:aNotification method, and into an action method which I connected to a button in the main window of my application.
On startup, the mainWindow was nil, but when I click the button (after everything is up and running and displayed), the mainWindow was no longer nil.
NSApplication provides other methods which you may be useful to you:
- windows - an array of all the windows;
– keyWindow - gives the window that is receiving keyboard input (or nil);
– windowWithWindowNumber: - returns a window corresponding to the window number - if you know the number of the window whose contents you wish to replace you could use this;
– makeWindowsPerform:inOrder: - sends a message to each window - you could use this to test each window to see if it's the one you are interested in.
With regard to calling methods on the delegate, what you say gives a warning works fine for me. For example, this works with no warnings:
NSLog(#"%#", [[[NSApplication sharedApplication]delegate] description]);
What exactly is the warning you receive? Are you trying to call a method that doesn't exist?
Fighting with MacOS just figured this out.
Apple's quote:
mainWindow
Property
The app’s main window. (read-only)
Discussion
The value in this property is nil when the app’s storyboard or nib file has not yet finished loading. It might also be nil when the app is inactive or hidden.
If you have only one window in your application (which is the most used case) use next code:
NSWindow *mainWindow = [[[NSApplication sharedApplication] windows] objectAtIndex:0];
Promise it won't be nil, if application has windows.
Swift flavored approaches for getting the main window (if present)
Application Main Window
guard let window = NSApplication.shared.mainWindow,
else {
// handle no main window present
}
// ... access window here
Application Window Array
let windowArray: [NSWindow] = NSApplication.shared.windows
guard windowArray.count > 0 else {
// hand case where no windows are present
}
let window = windowArray[0]
// ... access window here
If the window property isn't set yet, try delaying things until the app has finished loading, like so:
[myObject performSelector:#selector(theSelector) withObject:nil afterDelay:0.1];

Opening a new window and waiting for it to close

I have a Mac OS X app written in objetive-c Cocoa. You can see most of the code in this previous question. Essentially you click a button on the main window (the app delegate) and it opens another window where the user can enter information.
In the following code (that gets called when the user press the button in the app's main window)
- (IBAction)OnLaunch:(id)sender {
MyClass *controllerWindow = [[MyClass alloc] initWithWindowNibName:#"pop"];
[controllerWindow showWindow:self];
NSLog(#"this is a log line");
}
The NSLog line gets printer immediately after I called showWindow. Is there any way to wait until controllerWindow is closed to continue with the NSlog?
The reason for this is that the user set's a value on the new window I opened and I need to collect that value on the same OnLaunch so I need to wait.
I know that modal windows are bad form in Mac, but I have no control over this feature.
I've tried with
[NSApp runModalForWindow:[controllerWindow window]];
and then setting the popup window to
[[NSApplication sharedApplication] runModalForWindow:popupwin];
and it works but then the focus never gets passed to the main window anymore
Thanks!
If you want the window to be modal for your application, use a sheet: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Sheets/Tasks/UsingCustomSheets.html
However, there is no way to suspend execution of a method while the sheet is displayed, this would be tantamount to blocking the current run loop. You would have to break you code into the begin and end methods as described in the linked documentation.
Here are the steps you need to follow:
In TestAppAppDelegate create an NSWindow outlet to hold your sheet and an action to dismiss the sheet
Create a nib with an NSWindow as the root object. I think you already have this in "pop". Set the Visible at Launch option to NO (this is very important)
Set the file's owner of this nib to TestAppAppDelegate and connect the window to your new outlet, and the close button to your new action
In your method to launch the sheet (OnLaunch), use the following code:
(ignore this it's to make the code format properly!)
if(!self.sheet)
[NSBundle loadNibNamed:#"Sheet" owner:self];
[NSApp beginSheet:self.sheet
modalForWindow:self.window
modalDelegate:self
didEndSelector:#selector(didEndSheet:returnCode:contextInfo:)
contextInfo:nil];
Your close button action should be [NSApp endSheet:self.sheet];
Your didEndSheet: method should be [self.sheet orderOut:self];
You can use UIVIew method animateWithDuration:delay:options:animations:completion: to accomplish this.
You said you want the next line to execute once the window is closed, rather than after it is opened. In any case, you may end the OnLaunch method this way:
- (IBAction)OnLaunch:(id)sender {
MyClass *controllerWindow = [[MyClass alloc] initWithWindowNibName:#"pop"];
[controllerWindow animateWithDuration:someDelay:options: someUIAnimationOption
animations:^{
[controllerWindow showWindow:self]; // now you can animate it in the showWindow method
}
completion:^{
[self windowDidFinishShowing]; // or [self windowDidFinishDisappearing]
}
}
- (void) windowDidFinishShowing {
NSLog(#"this is a log line");
}

How to open a window on click on NSStatusItem?

I'm pretty new to cocoa, so please excuse me for any stupid mistakes I make.
I have a NSStatusItem, which I want to use to open up a menu. However as far as I know and have heard across different forms, without a custom view you are restricted to just a pop down menu. Is this true? And if so how do you make a custom view to do something (e.g. open a window in my case)? Thanks for any help.
No, it is not true. You need to set up the target and action for the status item to call a method which does what you want (opens the window).
// This goes where you set up the status item
NSStatusItem *statusItem; // You need to get this from the status bar
[statusItem setTarget:self];
[statusItem setAction:#selector(openWindow:)];
// This method is called when the status item is clicked
- (void)openWindow:(id)sender {
NSWindow *window = [self window]; // Get the window to open
[window makeKeyAndOrderFront:nil];
}
You may also want to call [NSApp activateIgnoringOtherApps:nil]; to your openWindow: method to ensure that the window you open is not behind some other application's window.

Loading NIB using [NSBundle loadNibNamed:owner:] but the window does not appear in the foreground

I wrote a menu application that has no persistent window or standard menu. When another application has focus and I use the menulet to trigger a window to be opened, it appears behind the foreground application (but above anything else that is present on the screen).
Basically...
-(IBAction)aboutWindow:(id)sender {
[NSBundle loadNibNamed:#"About" owner:self];
}
Can anyone point me in the right direction so I can get this window to appear above all other applications when it is initially spawned?
[Edit]
I have tried using a custom NSWindowController with the window linked up, and awakeFromNib calling a makekeyandorderfront method, but that wasn't doing anything.
I now have instead of the NSBundle call:
NSWindowController* awc = [[NSWindowController alloc] initWithWindowNibName:#"About"];
[[awc window] makeKeyAndOrderFront:nil];
And that spawns the window, but still does not make it in the foreground
Figured it out. Nothing was wrong with the Window, it was the Application. It was not in the foreground because of its nature as a menulet with no windows before this one is spawned. Final code:
-(IBAction)aboutWindow:(id)sender {
NSWindowController* awc = [[NSWindowController alloc] initWithWindowNibName:#"About"];
[[awc window] makeKeyAndOrderFront:nil];
[[NSApplication sharedApplication] arrangeInFront:nil];
}
You could try makeKeyAndOrderFront:
For example, in the About window's controller - assuming the controller had a reference to the window as myWindow:
- (void)awakeFromNib
{
[myWindow makeKeyAndOrderFront:nil];
}