One App Delegate, Two Windows - objective-c

I have a simple Cocoa app. It has two windows, each in a separate xib file:
MainMenu.xib
SecondaryWindow.xib
I have an AppDelegate class, which has a reference to the window in MainMenu.xib. I'm trying to make it have a reference to the window in SecondaryWindow.xib. I'm confused about how to do this. I have made an outlet, as such:
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (assign) IBOutlet NSWindow *secondaryWindow;
#end
Here's the implementation:
#implementation AppDelegate
#synthesize window = _window;
#synthesize secondaryWindow = _secondaryWindow;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[NSBundle loadNibNamed:#"SecondaryWindow" owner:self];
NSLog(#"_window = %#", _window);
NSLog(#"_secondaryWindow = %#", _secondaryWindow);
}
#end
_secondaryWindow is always (null)
I've add an outlet from in SecondaryWindow.xib connection the second window to the outlet in AppDelegate. What else do I need to do in SecondaryWindow.xib to make the connection complete?
EDIT: added [NSBundle loadNibNamed...]

You need to load it...
By default, MainWindow.xib is loaded by the framework, which creates its own instance of the app delegate.
You should load your second window from your app delegate (try [NSBundle laodNibNamed:#"SecondaryWindow" owner:self]. when you do this, the File's owner will be the application delegate - change the class of the file's owner in interface builder to reflect that and make your connections to it)

Did you set the type of File's Owner in the secondary window's .xib to the type of your app delegate? And did you then connect the window in that .xib to the secondaryWindow outlet of File's Owner?
If you did those things, and if the .xib is properly included in the project and you've specified the name of the file correctly in the +loadNibNamed:owner: message, then your secondaryWindow property should be populated.

Related

Reference outlet to main NSWindow in AppDelegate (OSX)

When you create a new project, I seem to remember you being able to access the basic blank window it creates in the storyboard using self.window in the AppDelegate methods.
I can't seem to set a reference outlet to the AppDelegate.h file via the storyboard either. Am I just hallucinating or is there no way to access the window/controller from AppDelegate?
Am I mixing up iOS and OSX practices?
Using storyboards, it seems impossible to connect the WindowController to AppDelegate. I used the following to get a reference to the main window:
AppDelegate.h
#property (weak) NSWindow* window;
AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_window = [[[NSApplication sharedApplication] windows] firstObject];
}
See this question for swift solution. Hope this helps :)
It's working for me.
I added an IBOutlet to my AppDelegate.h
#property (nonatomic) IBOutlet NSWindow *window;
Then in your Interface Builder (MainMenu.xib for me) select App Delegate:
Your IBOutlet should be in the Connection inspector to the right:
Maybe the connection between your .xib and your AppDelegate is lost?

Loading xib window from status menu

I have MainMenu.Xib which has a status menu element. The MainMenu File Owner is mapped to AppDelegate.
I also have another Xib which is a Window and it's File Owner is mapped to a ViewController with the same name.
So what I have tried and it kind of works is I have created an action in the appDelegate and have mapped the menu item in the status menu to the action in the appDelete using the First Responder.
In the action I have put:
SubscriptionsViewController *vc = [[SubscriptionsViewController alloc] initWithNibName:#"Subscriptions" bundle:nil];
[vc view];
If I step through the code it the window shows up but then goes away. So I have two questions
1) I thought there was a way to load the xib with NSMenuItem without the need of the above code.
2) How do I keep the window from closing right away? Do I need to save the view pointer or something?
edit: format code.
1) I thought there was a way to load the xib with NSMenuItem without the need of the above code.
Since NSWindow is not inherited from NSView like in iOS (UIWindow:UIView), it makes no sense to use NSViewController to load window from a xib. Use subclass of NSObject instead.
#interface SubscriptionsViewController : NSObject
#property (assign, nonatomic) IBOutlet NSWindow *window;
#end
#implementation SubscriptionsViewController
- (id)init
{
self = [super init];
if (self) {
[NSBundle loadNibNamed:#"Subscriptions" owner:self];
}
return self;
}
#end
2) How do I keep the window from closing right away? Do I need to save the view pointer or something?
It depends on the context to retain the instance of subscriptionsViewController or not. You can use below code to display a window, where the instance of window is in nib -
self.subscriptionsViewController = [[SubscriptionsViewController alloc] init];
[self.subscriptionsViewController.window makeKeyAndOrderFront:self];
Remember if "Visible At Launch" is set in nib, then the window is visible when you instantiate subscriptionsViewController.
You may add your window into the MainMenu.xib instead of using an addition xib file and create an outlet in the AppDelegare.h as
#property (assign) IBOutlet NSWindow *window;
Then all you need to do is
window.isVisible = !window.isVisible;
in the necessary action method...

Show NSWindow from separate nib (not modal)

How to do it? I simply want to load a window and show it in front of the main window.
NSWindowController* controller = [[NSWindowController alloc] initWithWindowNibName: #"MyWindow"];
NSWindow* myWindow = [controller window];
[myWindow makeKeyAndOrderFront: nil];
This code shows the window for one moment and then hides it. IMHO this is because I don't keep reference to the window (I use ARC). [NSApp runModalForWindow: myWindow]; works perfectly but I don't need to show it modally.
Yes, with ARC if you don't hold a reference to the window it will be torn down as soon you as you exit the routine you were in. You need to hold a strong reference to it in an ivar. [NSApp runModalForWindow: myWindow] is different because the NSApplication object holds a reference to the window as long as it is being run modally.
You should likely do something similar to the following, which creates a strong reference to the NSWindowController instance you create:
.h:
#class MDWindowController;
#interface MDAppDelegate : NSObject <NSApplicationDelegate> {
__weak IBOutlet NSWindow *window;
MDWindowController *windowController;
}
#property (weak) IBOutlet NSWindow *window;
#property (strong) MDWindowController *windowController;
- (IBAction)showSecondWindow:(id)sender;
#end
.m:
#import "MDAppDelegate.h"
#import "MDWindowController.h"
#implementation MDAppDelegate
#synthesize window;
#synthesize windowController;
- (IBAction)showSecondWindow:(id)sender {
if (windowController == nil) windowController =
[[MDWindowController alloc] init];
[windowController showWindow:nil];
}
#end
Note that rather than sending the makeKeyAndOrderFront: method directly to the NSWindowController's NSWindow, you can just use NSWindowController's built-in showWindow: method.
While the above code (and sample project below) use a custom subclass of NSWindowController, you also use a generic NSWindowController and create the instance using initWithWindowNibName: (just make sure the File's Owner of the nib file is set to NSWindowController rather than a custom subclass like MDWindowController).
Sample project:
http://www.markdouma.com/developer/MDWindowController.zip

Mac Development Building Basic Interface

I am going through several example code banks and tutorials and just completely stumbling, a lot of it is due to the fact that these tutorials are written for Xcode < 4.2 and ARC changes a few things.
I am attempting to build an interface with an MVC design pattern. I am using the basic template provided for the Application Delegate. I have added a class called MainWindowController which inherits from NSWindowController. In the Interface Builder, firstly I removed the Window object in the MainMenu nib file (because I want it in a separate file). I create a new Interface called MainWindow(.xib) I change the file owner to MainWindowController I add the Delegate to the Object List. Now at this point something is not clicking.
I do not fully grasp how or what I need to implement in order for the Delegate to essentially load and launch the Window Controller. First I tried linking the outlet for "delegate" in the Window to the actual application delegate (called AppDelegate) and then linking the Window Outlet in the Delegate class to the Window in Interface Builder.
I would like an answer to this but I would be far more happy with the correct documentation describing this process. I'm sure there is something on MacDev but I'm having trouble finding it.
Here's what I'm working with:
#class MainWindow;
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (strong) MainWindow *mainWindowController;
#property (assign) IBOutlet NSWindow *window;
#property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;
#property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
#property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
- (IBAction)saveAction:(id)sender;
#end
...
#implementation AppDelegate
#synthesize window;
#synthesize mainWindowController;
#synthesize persistentStoreCoordinator = __persistentStoreCoordinator;
#synthesize managedObjectModel = __managedObjectModel;
#synthesize managedObjectContext = __managedObjectContext;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
MainWindow *controller = [[MainWindow alloc] initWithWindowNibName:#"MainWindow"];
mainWindowController = controller;
// ... the rest handles the ManagedObject Models...
Solution to date:
#synthesize mainWindowController = _mainWindowController; // IBOutlet is linked in IB
//...
- (void) applicationDidFinishLaunching: (NSNotification *) aNotification
{
//... initialization of event handling etc...
if ( !_mainWindowController ) _mainWindowController = [[ MainWindowController alloc] initWithWindowNibName:#"MainWindow"];
[_mainWindowController showWindow: self];
// ...
The application delegate can manage an NSWindow in the main nib file or it can delegate that task to a controller (NSWindowController) which is typical of document based applications or MVC design patterns. The default nib file specified by the Basic-info.plist defines which nib file is loaded. Typically the default nib should be the main menu, which loads the delegate as well. The delegate should then by means of applicationDidFinishLoading: or awakeFromNib: perform initialization of the controllers and continue appropriate delegation in accordance with the delegation and mvc design patterns.
The main problem I was having was launching the window, which is done by showWindow: . The exacerbation of this problem stemmed from source code from a very old Mac OS X project that used deprecated functions and methods to accomplish delegation and led me down the wrong path. Thanks for the answer, it ultimately had me look in the right place for the right questions and I found the right answer.
Your problem is that in that second xib, "MainWindow", a new AppDelegate object is being created that has absolutely nothing to do with the AppDelegate object in the "MainMenu" xib. The objects in each xib are real objects that get serialized and then loaded at runtime.
This specific issue will be addressed in the Resource Management Guide, "Nib Loading". I can also suggest "Core Application Design".

Cocoa - Link IBOutlet to Separate Nib

I have a nib file in which I load at a certain point in my application. Would it be legal for me to link a NSWindow IBOutlet from my AppDelegate to the 2nd nib file's window? In other words, my IBOutlet is not being connected to the MainMenu xib file that Xcode creates on default. If this was legal, can I have access to the NSWindow's frame and other features?
Yes you can do that. In your second nib file, I would use a NSWindowController as the file's owner to the nib. Then in your AppDelegate, create an instance of the NSWindowController and then load the nib. From there, you can inspect the properties of the window owned by NSWindowController or do whatever you want with the window.
Here is an example
#interface MyAppDelegate : NSObject
{
NSWindowController *myWindowController;
}
#end
#implementation MyAppDelegate
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification
{
myWindowController = [[NSWindowController alloc] initWithWindowNibName:#"MySecondWindow"];
[[myWindowController window] center];
[[myWindowController window] makeKeyAndOrderFront:self];
}
#end
In your second nib, set the File's Owner to be your app delegate class. Then attach the outlets as needed within IB. At run time, call [NSBundle loadNibNamed:owner:] and be sure to pass self as the owner.
Yes, this would be legal as long as the App Delegate is the File's Owner of the nib you are loading. That said, if you unload the nib later, you have to make sure that all top level objects in the nib are properly released (otherwise you'll create a memory leak).