Is it possible to have a custom NSWindowController on launch? - objective-c

In this answer is said it is possible to have a custom NSWindowController by removing the window from MainMenu.xib and instantiating the window controller 'manually' from applicationDidFinishLaunching:. But it also says:
To stop the default window from showing I just delete it. There's probably a better way but I don't know that.
Is there a better way? What is that better way, should it exist? Is it considered 'normal' practice to get your own window controller?

To do this, you would usually subclass NSWindowController and change the File's Owner class to your WindowController subclass in the nib.
EDIT:
If you aren't doing a document-based app, and just want an NSWindowController of your own to do on-demand loading of Nibs (completely reasonable), then you'd delete the window from your nib and instantiate an NSWindowController subclass programmatically, using it explicitly to do your window loading...
#implementation MyApplicationDelegate {
MyWindowControllerSubclass *windowController;
}
-(void)applicationDidFinishLaunching:(NSNotification *)notification {
windowController = [[MyWindowControllerSubclass alloc] initWithWindowNibName:#"MyWindowNib"];
[windowController showWindow:nil];
[windowController.window makeKeyAndOrderFront:nil];
}

I was running into the same issue and I want to show you my own solution.
Create a normal Cocoa Application (not Document Based)
Go to MainMenu.xib an delete the Window
Go ahead and create a new file, User Interface -> Window
After that create a subclass of NSWindowController
Open the just created xib file and set the Custom Class in the Identity inspector to the just created subclass of NSWindowController
Right click on File's Owner and connect the window property to the actual window
Now go to the AppDelegate an create an instance variable that holds you CustomWindowController
Last thing you have to do is instantiate your CustomWindowController self.customWindowController = [[AccountWindowController alloc] initWithWindowNibName:#"CustomWindow"]; and show the Window [self.customWindowController showWindow:nil] in - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
Here is an example project https://www.dropbox.com/s/ft3t7w72806tnoe/CustomWindowController.zip

I actually found another way: NSWindowController has the method -initWithWindow:. Because the App Delegate has a property window which is linked to the window from MainMenu.xib on startup, it was easy to link it to my WindowController:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
MyWindowController *wincon = [[MyWindowController alloc] initWithWindow:window];
}
I have yet to research this, but I don't get any errors.

Related

How can I set a menu item click to respond to IBAction from another class?

I'm pretty new to XCode/Objective-C/Cocoa. I want to implement a settings window for my app.
I have a MainMenu.xib which also holds my main Window. From the menu, I want to open a settings window. I created Settings.xib and appropriate .h and .m files to hold what that window would do.
Settings.h:
#import <Cocoa/Cocoa.h>
#interface Settings : NSWindowController <NSApplicationDelegate>
-(IBAction)openSettings:(id)senderId;
#property (nonatomic, retain) Settings *thisWindow;
#end
Settings.m:
#import "Settings.h"
#implementation Settings
- (void)windowDidLoad {
[super windowDidLoad];
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
// open preferences window
- (IBAction)openSettings:(id)senderId
{
_thisWindow = [[Settings alloc] initWithWindowNibName:#"Settings"];
[_thisWindow showWindow:self];
}
#end
I dragged my Preferences menu item to first responder, and selected openSettings: from there.
However, the item is still disabled and I'm pretty sure it's because I did nothing to link the Settings interface to my MainMenu.xib, which works with AppDelegate.h/m.
How do I make this work? All other solutions I found didn't quite work for me.
If I understand you clear you want to store your MainMenu and MainWindowController in a two separate classes.
Open your main menu nib-file. Remove window from the object tree.
Check in Project Settings -> General -> Main interface is still your MainMenu (without .xib-extension).
Create (implement) your custom MainWindowController class (with a nib-file may be).
Open AppDelegate class. In - (void)applicationDidFinishLaunching:(NSNotification *)aNotification method create an instance of main window controller class, show the window
Use this code below
MainWindowController *controller=[[MainWindowController alloc] initWithNibName:#"MainWindowController"];
[controller showWindow:nil];
[controller.window makeKeyAndOrderFront:nil];
Here it is.
Okay so in your mainwindowcontroller, declare a property of type NSWindowController *settingsWindow. Init it with the corresponding xib.
Then create a method called -(void)openSettings, with one line [self.settingsWindow showWindow:self];
Then also in your mainWindowController initialization, init a NSMenuItem, and set it's action to openSettings. Then add that NSMenuItem to the Mainmenu where you'd like programmatically, like this
//mainMenu is your application's menu-- if you switched index to 1 it would be the 'File' menu
NSMenu *mainMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu];
[mainMenu insertItem:newItem atIndex:4];
I ended up using my AppDeleate.m to open the dialog instead. I linked the menu button to the AppDelegate object in the interface builder, and used openSettings:. Here's how it looks:
// open preferences window
- (IBAction)openSettings:(id)senderId
{
_settingsWindow = [[Settings alloc] initWithWindowNibName:#"Settings"];
[_settingsWindow showWindow:self];
}
In AppDelegate.m, instead of Settings.m.

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

Swapping views - NSWindowController and NSViewController(s)

I'm very new in Mac OS programming. At the moment I'm trying to create simple measurement application which will have one window with the toolbar at the top and the appropriate view in the bottom. Clicking button in the toolbar should result in switching view below it - e.g. clicking on the "Connection" button will show with connection settings, "Measurements" will show current data from the device.
The problem is - I don't know how to handle swapping views, maybe in other words - something I know but not exactly...
I found similar discussion here: NSViewController and multiple subviews from a Nib but there is no answer how to create NSWindowController and how to assign it to the Main window. Because I guess it is necessary to create NSWindowController to be able to swapping views. If I'm wrong, please correct me.
So I'm creating new project (called Sample here) and there is SampleAppDelegate.h file, which looks like:
#interface SampleAppDelegate : NSObject <NSApplicationDelegate> {
#private
NSWindow *window;
}
#property (assign) IBOutlet NSWindow *window;
#end
There is window ivar, which holds the only one window, created from the MainMenu.xib (as I think).
So how should I create NSWindowController for the window from the SampleAppDelegate?
Should I just create my WindowController subclass and in the function
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
of the SampleAppDelegate like this:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
MyWindowController *wc = [[MyWindowController alloc] initWithWindow:self.window];
[wc showWindow:self];
self.myWindowController = wc;
[wc release];
}
I'll be very grateful for any hints and help.
Marcin
You shouldn't need an NSWindowController to do view swapping, NSWindowController used (I think) just when you need multiple toplevel windows.
You can just subclass NSViewController for each type of view that you want, put each view into a nib, and call -(NSView *)view when you need a view to put into the bottom part of the window. You should be able to just add it to the window like normal, or put it in an NSBox by using setContentView:view
For your two views you'd create MeasurmentsViewController and a ConnectionViewController. Then you'd create your views in MeasurementsView.nib and ConnectionView.nib, and use those nibs to initialise your view controllers.
Then in your main window, if you were to put an NSBox, if you wanted to put the MeasurementsView into it
NSView *measurementsView = [measurementsViewController view];
[boxAtBottomOfWindow setContentView:measurementsView];
and to put the ConnectionView into it
NSView *connectionView = [connectionViewController view];
[boxAtBottomOfWindow setContentView:connectionView];

Pointer to NSWindow Xib after loading it?

In my code below, CustomWindow is a subclass of NSWindow.
CustomWindow *window = [[CustomWindow alloc] init];
if (![NSBundle loadNibNamed:#"NibName" owner:window])
[window center]; // doesn't work
How do you get a pointer to control your XIB after you load it so you can do things such as centering the NSWindow (I mean the serialised one that resides inside the XIB)?
What am i doing wrong here?
You should be using an NSWindowController subclass. NSWindowController is specifically designed to do exactly what you want to achieve and solves several problems that you will run into if you load the nib directly using the methods of NSBundle. You generally should always use an NSWindowController subclass to manage windows.
Create a subclass of NSWindowController:
#interface MyWindowController : NSWindowController {}
#end
#implementation MyWindowController
- (id)init
{
self = [super initWithWindowNibName:#"MyWindow"];
if(self)
{
//initialize stuff
}
return self;
}
//this is a simple override of -showWindow: to ensure the window is always centered
-(IBAction)showWindow:(id)sender
{
[super showWindow:sender];
[[self window] center];
}
#end
In Interface Builder, set the class of File's Owner to be MyWindowController and connect the window outlet of File's Owner to the window object in your nib.
You can then display the window by doing this:
MyWindowController* controller = [[MyWindowController alloc] init];
[controller showWindow:self];
In my code below, CustomWindow is a subclass of NSWindow.
CustomWindow *window = [[CustomWindow alloc] init];
if (![NSBundle loadNibNamed:#"NibName" owner:window])
[window center]; // doesn't work
How do you get a pointer to control your XIB after you load it so you can do things such as centering the NSWindow inside the XIB?
“centering the NSWindow inside the XIB” makes no sense (you would center it on the screen), unless you mean centering the NSWindow object that is inside the xib, in which case, why are you creating another NSWindow (CustomWindow) object outside of the xib?
Remember that a nib (or xib) is an archive of objects. If you want to use a window that you have in your nib, you need to create an outlet to point to that window, set the class of the File's Owner to be the class where you've added the outlet, hook up the outlet in IB, and appoint the object with the outlet as the File's Owner by passing it to the owner: argument. That object, as the owner, will then be responsible for working with the window. It may be (usually is, in my code) the same object that loads the nib.
Also, init doesn't work on NSWindow; you must use initWithContentRect:styleMask:backing:defer: or initWithContentRect:styleMask:backing:defer:screen:. Using init would only be valid if you've implemented init yourself in CustomWindow, and used one of those two selectors for the [super init…] message.
You probably don't want to make your window the File's Owner. Normally you would pass self or some controller object there. Then if self or that controller object has a CustomWindow IBOutlet, it will get hooked up when you call loadNibNamed:. Check out this post for example code.
A XIB is a container for objects, it's not equal to a window. You can't center a XIB, you can only center a window contained in a XIB.
Also, the objects in the XIB are created when you load it. You don't pass an object as owner that then stands in for one of the objects in the XIB, you instead use IBOutlets to get references to the new objects created when loading the XIB and then you can interact with them.
The File's Owner object is a special object in XIBs, as it's the only object that is not created and that you can specify by passing it to loadNibNamed:owner:. It's your gateway between the XIB-created objects and your application.
Usually, the owner object is some kind of controller class. Set the File's Owner's class in Interface Builder to your controller class, then define some IBOutlets in the class, they will show up in Interface Builder on the File's Owner and you can connect your objects in the XIB to them.
Finally, when you pass your controller object to loadNibNamed:owner:, Cocoa will connect your IBOutlets to the newly created objects and you can use them to interact with them, e.g. to center a window in your XIB.

Objective C Delegate for the Main Application Window

So I'm trying to do this exercise where I need to set a delegate for the main window. The purpose is to make sure that when the user resizes the window, it's always twice as wide as it is high.
This is my AppController.h file:
#import <Cocoa/Cocoa.h>
#interface AppController : NSObject
{
NSWindow *windowWillResize;
}
#end
and this is my AppController.m file:
#import "AppController.h"
#implementation AppController
- (id) init
{
[super init];
windowWillResize = [[NSWindow alloc] init];
[windowWillResize setDelegate:self];
return self;
}
- (NSSize) windowWillResize:(NSWindow *)sender
toSize:(NSSize)frameSize;
{
NSLog(#"size is changing");
return frameSize;
}
#end
However, I can remove the line
[windowWillResize setDelegate:self];
since I set the delegate in Interface Builder, but I'm not sure why this works.
How does windowWillResize know that I'm referring to the main application window since I'm doing a completely new
windowWillResize = [[NSWindow alloc] init];
I have a feeling that I am completely doing this wrong. Could someone point me in the right direction? Thanks!
Indeed, you don't need to create a NSWindow *windowWilResize since a newly created Cocoa app already has a main window. You don't need to implement an -init method either.
You only need to set you appController as a delegate of your main window in Interface Builder and to implement the -windowWillResize: method in your appController.
If you are familiar with french language, you can take a look at a blog entry I have written on this subject: Délégation en Cocoa.
You're leaking an instance of NSWindow. In -init you create an NSWindow instance. However, that is not used because when the NIB loads, it sets up all the connections that you specified in Interface Builder and you start using the window from the NIB instead. Do not create a window object in code - Interface Builder does it for you! :-)
In fact, it's not quite "instead"; your app controller is now the delegate for both NSWindow instances - the one that comes from the NIB and the one you instantiated in -init. However as the in-code NSWindow is never used anywhere else, it's still redundant and should be removed.
If you just want to maintain the aspect ratio of the window you can use either of these two NSWindow methods:
setAspectRatio:(NSSize)
setContentAspectRatio:(NSSize)
The first method locks the entire window size, including the title bar. The second one just the content. You can call this method during the initialization of your window inside the delegate (for example: -applicationDidFinishLaunching)