How to give focus to NSWindow loaded from NIB? - cocoa

I'm using NSWindowController to load a window from a NIB. However, when I call showWindow:, the window is visually topmost, but the focus remains where it was (instead of moving it to the new window).
It's easy to see this happening when the first window (with keyboard focus) is moved slightly, before creating the new window (via cmd+n). This is the result:
The bottom, focused window is the original window. The unfocused window on top is the newly created window.
This is the relevant code:
AppDelegate.h:
- (IBAction)newDocument:(id) sender;
AppDelegate.m:
- (IBAction)newDocument:(id) sender {
[[[FooController alloc] init] showWindow:self];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[self newDocument:self];
}
FooController.h:
#interface FooController : NSWindowController { }
#end
FooController.m:
- (id)init {
self = [super initWithWindowNibName:#"FooWindow"];
return self;
}
FooWindow.xib:
A freshly created Window xib, without modifications.
MainMenu.xib:
The default MainMenu.xib, with its window deleted.
Calling makeKeyAndOrderFront: on the window in the controller's windowDidLoad method does not appear to focus the new window. Setting the File's owner of FooWindow.xib to FooController also did not appear to help.
What is the correct way to load and show a window from a NIB so that it does receive keyboard focus?
Edit: It looks like NSWindowController's window method returns nil, which explains why calling methods on window doesn't do anything. But why is it nil?

Okay, I found the cause of this problem.
The xib's File's owner must be set to the controller, and (this is the part I didn't know about) you have to connect the controller's window outlet to the window itself.
Having done that, it just works. No makeKeyWindow, makeMainWindow or makeKeyAndOrderFront: needed.

Perhaps makeMainWindow: or makeKeyWindow: helps

Related

Print is printing entire window, not the View

I have a simple program, for making sure the print works.
-Subclassed NSObject for a Controller, "ViewController.
-Subclassed NSView, View
Added a custom view to the window, set class to View.
Made ViewController delegate for View.
Added Object in IB, set class to ViewController.
Added IBOutlet to Delegate section of ViewController, connected it to the the custom view on the window.
In View, have a simple [myString drawInRect: rect], where myString is defined in the init as #"Hi".
When I run, the program prints "Hi" in the view. When I click the "Print" menu item, the print preview shows the entire window.
More: I hade an earlier test program which had no print code, I ran it and the print preview showed only the view. I've got through both codes and cannot find a difference, so I am lost at why one is working and the other is not.
Do any of you know why the print preview would show the entire window instead of the view?
[EDIT]-----
I also created an extremely simple program to check and have the same issue.
Subclassed NSView, MainView
Added Custom View to Window, set class to MainView
Added [str drawInRect: dirtyRect withAttributes:nil];
(note; NSString *str = #"Hello";).
[More Information]-------
I added a print method as follows to the NSView object:
-(void)printPDF{
NSRect r = [self bounds];
[[NSPrintOperation printOperationWithView:self] runOperation];
[self dataWithPDFInsideRect:r];
}
Added a button to the window, linked it to an IBAction method in the ViewController:
-(IBAction)printToPDF:(id)sender{
[view printPDF];
}
In the ViewController I have:
IBOutlet View *view;
This works! So, why does the default "print" menu item print the entire Window?
By default, you don't have to write any printing code and the printing just works. But, it provides default behavior (aka, print the entire window).
If you want custom printing behavior, you'd have to write your own printing method.(like you did in your extremely simple program). And you can link the default print menu item to your own printing method.

OS X second window won't stay open

I want to open a second window to act as a content editor for some of the fields in the main window of my app. I created a custom NSWindowController (called ItemEditor) with its own nib.
I open the new window with this code:
ItemEditor *editor = [[ItemEditor alloc] initWithWindowNibName:#"ItemEditor"];
[editor showWindow:nil];
[editor.window makeKeyAndOrderFront:nil];
The new window appears for an instant and then immediately disappears. Both the initWithWindow: and windowDidLoad of ItemEditor are called, but windowWillClose: isn't.
Can anyone tell me what's going on here? I'm stumped.
What's happening is that you're using ARC... and nothing is holding onto your "editor" object after it's created. That's why it's disappearing as soon as it's being created.
You need to make "editor" a "strong" property in your parent window controller.
In other words, declare it like this in the parent view controller's .h file:
#property (strong) ItemEditor *editor;
And replace the first line of your code snippet above with this:
self.editor = [[ItemEditor alloc] initWithWindowNibName:#"ItemEditor"];

NSTextView won't become first responder

Here's the layout of my little test app:
AppDelegate owns WindowController.
WindowController owns CustomTextContainerView.
CustomTextContainerView owns an NSScrollView which embeds MyCustomTextView (an NSTextView subclass).
The xibs for both the standard MainMenu and my window controller are relatively empty. My window controller's -windowDidLoad looks like this:
- (void)windowDidLoad {
[super windowDidLoad];
// create CustomTextContainerView
[[self window] setContentView:self.customTextContainerView];
}
What I'm trying to do is set first responder to the textView, but I've tried everything I can think of to get this to work.
I've made it so CustomTextContainerView just forwards -becomeFirstResponder on to its textView. I've tried calling it directly on both the container and the textView but I can't get it to become first responder automatically.
Note: The user can still click in the text area and start typing, but what I'm trying to do is set first responder status automatically so I don't have to click before I start typing. What am I missing?
To force the first responder for a window, call this:
[[self window] makeFirstResponder:self.customTextContainerView];
(This assumes that everything else necessary for first-responder status is enabled, e.g. the view can't have overridden acceptsFirstResponder to return NO.)

Receive window notifications

I have an NSWindow set up in Interface Builder. I have set the class of File's Owner to my NSWindowController and linked the window property of the controller to my NSWindow.
My controller implements NSWindowDelegate.
Now, in my controller, I have added the following:
- (void)windowDidLoad
{
[super windowDidLoad];
[self.window setDelegate:self];
}
- (void)windowDidBecomeMain:(NSNotification *)notification
{
NSLog(#"Did become main.");
}
Still, -windowDidBecomeMain: isn't called. Does anyone know why this is?
EDIT:
Trying to show a window from AppDelegate on launch. The main nib (declared in Info.plist) contains a menu item only which is linked to the AppDelegate. In the application delegate, I show an icon on the status bar and when this icon is clicked, I display the menu from the main nib.
In the application delegate, I also want to display a window which should have a window controller assigned to take care of the logic.
I believe that when this works, I will receive my window notifications.
Now, the following code doesn't show the window and I can't figure out why.
DemoWindowController *dwc = [[DemoWindowController alloc] initWithWindowNibName:#"DemoWindowController"];
[dwc showWindow:self];
Note that self is the application delegate.
I suspect your problem is due to the fact that your window controller is not actually the object that is the nibs file owner.
When you change the class in interface builder you are telling it what outlets and actions are available (which is why you are able to drag to the window outlet) but you are still responsible for passing in this object yourself.
In the case of a non-document based application, you will have a main method which calls NSApplicationMain. What this does is basically look up and load the window nib that is specified in your info.plist file and pass the current NSApplication instance to this nib as the files owner (so even though you changed the class type to NSWindowController, the object being passed in is actually of type NSApplication).
The easiest way to fix your problem is to get rid of your window controller for now (as it isn't actually doing anything yet).
You should implement the -windowDidBecomeMain: method in your app delegate. Then Ctrl+drag from your window to your appDelegate to set it as the delegate of the window to get your notifications.
Update
To answer your question regarding the WindowController beware of the following two issues:
You are creating your window controller variable (dwc) in your applicationDidFinishLaunching: method. This is released the moment you leave the method taking your window with it. Create an instance variable to hold onto the window controller instead.
Ensure that your second window nib has its file owner set to NSWindowController (or your window controller type) and that its window outlet is connected to the window in the nib file.
Your window should now display.

How to change the NSScreen a NSWindow appears on

I have an application that will load a couple of windows depending on which button is pressed. All except one of these open on the mainScreen (the screen in which the main window is open in). One of them (the preference window) opens on the first screen (the screen with the menu bar). I cannot understand way it is doing this, is there a way to change the screen that a NSWindow opens on?
I could not get toohtik's answer to work. What I ended up doing was subclassing NSWindow and then overriding constrainFrameRect: toScreen:. This will automatically open the new window on the "main screen" of the application.
- (NSRect)constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
{
AppDelegate *delegate = [[NSApplication sharedApplication] delegate];
return [super constrainFrameRect:frameRect toScreen:delegate.window.screen];
}
I dont't know why you have that behaviour but you can change it through initWithFrame method that takes NSScreen argument.

Resources