Show NSWindow from separate nib (not modal) - objective-c

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

Related

How to add a secondary window to a document-based app

I want to add a secondary window containing resources that can be dragged and dropped into NSDocuments.
My project contains:
1) ResourceWindow.xib
2) ViewController.xib
3) Main.storyboard
#interface AppDelegate ()
#property (nonatomic,strong)NSWindowController* wc;
#property (nonatomic, weak)NSWindow* resourceWindow;
#property (nonatomic, strong)ViewController* vc;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.wc = [[NSWindowController alloc]initWithWindowNibName:#"ResourceController.xib"];
self.resourceWindow = [self.wc window];
[self.wc showWindow:self];
self.vc = [[ViewController alloc]initWithNibName:nil bundle:nil];
[self.vc.view setFrame:[self.resourceWindow.contentView bounds]];
[self.resourceWindow.contentView addSubview:self.vc.view];
}
self.wc.window is nil immediately after allocating and initializing it.
Please set me straight on this.
Thanks
EDIT:
ResourceWindow.xib does not contain a window controller just a window . Is that the problem? Is the solution to drag and drop a custom object into the xib file and change it's class to NSWindowController?
In addition to the file name issue pointed to by Willeke, windows are nil until showWindow is called. Sigh. I started again by subclassing NSWindowManager and checking add xib file. Then, set the class of files owner to SourceWindowController and changed the code to:
#property (nonatomic,strong)SourceWindowController* wc;
#property (nonatomic, weak)NSWindow* resourceWindow;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
self.wc = [[SourceWindowController alloc]initWithWindowNibName:#"SourceWindowController"];
[self.wc showWindow:self.window];
self.resourceWindow = [self.wc window];

Error when using NSWindowController

I just started a new Cocoa project after a long time... And I don't know why, but I always get an error when calling a xib by a NSWindowController. What I do is really very simple: I have a new project as a starting point and then I don't wantz to call the xib from Appdelegate, but from a subclass of NSWindowController. Then the output tells me that:
2014-11-12 09:58:18.519 SimpleTest[8554:378690] ApplePersistence=NO
2014-11-12 09:58:18.671 SimpleTest[8554:378690] Failed to connect (window) outlet from (NSApplication) to (NSWindow): missing setter or instance variable
Okay, how does it look in code? My Appdelegate looks like this:
#import "AppDelegate.h"
#import "MainWindowController.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (strong) MainWindowController *mainWindowController;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
_mainWindowController = [[MainWindowController alloc] initWithWindowNibName:#"MainMenu"];
[self.mainWindowController showWindow:self];
}
#end
Nothing special so far. The MainWindowController looks like this:
#import "MainWindowController.h"
#interface MainWindowController ()
#property (weak) IBOutlet NSWindow *window;
#end
#implementation MainWindowController
- (id)initWithWindow:(NSWindow *)window
{
self = [super initWithWindow:window];
if (self != nil)
{
//do something
}
return self;
}
#end
And again very simple... Additiponally I have some modifications in IB: File'Owner of MainMenu.xib becomes MainWindowController. Its 'window' outlet is connected to the Window of the application. The delegate of Window is connected to File's Owner. Well, that's it! But why do I receive this error? What am I doing wrong?
---EDIT---
this shows the connections in IB
The most important is using right selector to create new instance and having all wired up correctly.
Steps:
1. Add new xib with window or an empty one and add window to it
2.Select File owner and set it to NSWindowController or its subclass.
Select window and connect it to File owner
Make new NSWindowController instance
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSWindowController *windowController = [[NSWindowController alloc] initWithWindowNibName:#"Window"];
[NSApp runModalForWindow:[windowController window]];
}

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...

Loading XIB file without a UIViewController

I'd like to design a UIView and some sub-views (UIWebView, UIToolbar, some UIBarButtonItems, a progress indicator and so-forth) using the Interface Builder, but I think it's unnecessary to do this traditionally, by using a UIViewController, using presentViewController:animated etc.
So, I created a custom class, with the .h file code as follows:
#interface FileInteractionManager : NSObject {
}
#property (nonatomic, retain) IBOutlet UIView *fileView;
#property (nonatomic, retain) IBOutlet UIWebView *fileWebView;
#property (nonatomic, retain) IBOutlet UIBarButtonItem *printButton;
#property (nonatomic, retain) IBOutlet UIBarButtonItem *optionsButton;
#property (nonatomic, retain) IBOutlet UIBarButtonItem *doneButton;
My .m file is as follows:
#implementation FileInteractionManager
#synthesize fileView, fileWebView, doneButton, optionsButton, printButton;
-(id)init {
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"FileInteractionView" owner:self options:nil];
NSLog(#"Load success!");
return self;
}
Finally, I create a stand-alone xib file named 'FileInteractionView.xib', change the file's owner to the custom class I created above, and wire up the IBOutlets.
When I call the init method on my class, I can see in the debugger that all my IBOutlet objects are instantiated properly.
My questions are:
Is the loadNibNamed:owner:options: method the right way to load my stand-alone .xib file? I don't like the fact that this method returns an array I have no use for (the top-level object returned matches my variable fileView, but I've already linked them through the Interface Builder).
Is my general approach correct in solving my problem? I carried out the above steps because I wanted a simple UIView object that I could add to my existing UIViewController, rather than present and dismiss a whole new UIViewController.
I use a little different approach. I create a subclass of UIView (MyCustomView i.e.) then the xib with the UI of the view and change the (main) view class the the one just defined. In the xib then you can link the outlet to the custom view itself (not the file owner).
Finally in the class definition I create a function like this:
+ (id) newFromNib
{
NSArray *nibArray = [[UINib nibWithNibName:NSStringFromClass([self class]) bundle:nil] instantiateWithOwner:nil options:nil];
return nibArray[0];
}
Just a couple of notes:
1) this's a class method, you can use "self" just for stuff like "NSStringFromClass([self class])" but the real object is the variable returned
2) this example suppose the xib have the same name of the class (via NSStringFromClass([self class]) so I can copy-paste it without changing anything ;) ) and that your view is the first one defined in the xib (the standard). If you store more than a "main" view inside one xib pick the right element.
so where I need MyCustomView I do something like:
MyCustomView* mycv = [MyCustomView newFromNib];
then set frame/center and add to superview...
I think this way is pretty usefull if you have a "library" of complex UI elements and want to design them via xib then add when needed.
il Malvagio Dottor Prosciutto answer is nice. Here is a possible alternative.
Load nib in NS_DESIGNATED_INITIALIZER and become owner of subview
If we accept the xib to only hold a subview instead of the view itself, then we can load the subview in initWithFrame: and keep an ownership construction in xib.
#interface MyCustomView ()
#property (strong, nonatomic) IBOutlet UIView *subview;
#end
#implementation MyCustomView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
[self addSubview:self.subview];
return self;
}
#end

NSWindowController: loadWindow loads window from nib but showWindow: does nothing

I have an NSWindowController subclass called _PreferencesWindowController with the following implementation -
#synthesize window;
- (id)init {
self = [super initWithWindowNibName:#"PreferencesWindow"];
if (!self) return nil;
return self;
}
And I tried to show the window in _PreferencesWindowController by using the following code -
_preferencesWindowController = [[_PreferencesWindowController alloc] init];
[_preferencesWindowController showWindow:nil];
It does nothing, and I checked _preferencesWindowController.window is nil from the debugger.
However if I call loadView on _preferencesWindowController the window can be loaded and is visible; _preferencesWindowController.window is no longer nil-valued -
[_preferencesWindowController loadWindow];
I looked at Apple's documentation on NSWindowController it specifically says "you should never directly invoke loadWindow", instead showWindow: should be used. I'm wondering what I might have missed that resulted in the above-mentioned behaviour I have been seeing.
OK I solved this by looking at the NSWindowController header file.
The problem is in my header file for _PreferencesWindowController -
#interface _PreferencesWindowController : NSWindowController <NSToolbarDelegate> {
NSWindow *window;
}
#property (assign) IBOutlet NSWindow *window;
#end
By removing the #property declaration and changing NSWindow *window ivar to IBOutlet NSWindow *window, showWindow: method now works without a glitch.
The property declaration must have resulted in an undefined behaviour in showWindow: method in NSWindowController's implementation.