NSWindow beginSheet completionHandler not called - macos

I am showing a sheet within my main window. I present the sheet using this code:
AddContactWindowController *addContact = [[AddContactWindowController alloc] initWithWindowNibName:#"AddContactWindow"];
addContact.currentViewController = myView;
self.addWindowController = addContact;
[self.view.window beginSheet: addContact.window completionHandler:^(NSModalResponse returnCode) {
NSLog(#"completionHandler called");
}];
AddContactWindowController is a NSWindowController subclass. It has a view controller within it. Inside the view is a "close" button which invokes this:
[[[self view] window] close];
This does close the window, but the completionHandler from beginSheet is not invoked. This causes me problems down the road.
Is there any particular way we should close the NSWindow sheet for the completion handler to be successfully called? I've also tried [[[self view] window] orderOut:self] but that doesn't work either.
Thanks.

You will want to call -endSheet:returnCode: on your window, rather than just ordering it out.

You must properly finish the modal session.
I used to call - (void)performClose:(id)sender and stop the modal session in the delegate method.
- (void)windowWillClose:(NSNotification *)notification {
[NSApp stopModal];
}
But for a sheet, endSheet looks more appropriate.
self.addWindowController = addContact;
[self.view.window beginSheet:self.addWindowController.window];
...
...
[self.view.window endSheet:self.addWindowController.window];
self.addWindowController = nil

Related

Can't set position of modal window

I have a window I want to display as a modal window (OS X 10.10). I'm loading the NIB for the window and am able to set the title successfully and then display the window. But whatever I do to try to affect the window position doesn't work.
This works (part of NSWindowController sub-class):
[[self window] setTitle:title];
[[NSApplication sharedApplication] runModalForWindow:[self window]];
Here are ways with which I've tried to affect the position after setting the title:
[[self window] setFrameOrigin: NSMakePoint(200.0, 200.0) ];
[[self window] setFrameTopLeftPoint: NSMakePoint(200.0, 200.0) ];
[[self window] setFrame: NSMakeRect(200, 300, [[self window] frame].size.width, [[self window] frame].size.height) display:YES];
(I've tried other values as well - just for testing, but nothing.)
I can even query the
[[self window] frame]
and it pretends to accept the new values, but the window stubbornly keeps showing up in the same position.
What gives?
I have solved this by
1- Make a new NSWindow subclass, overriding the center method, where you just make the frame of the new window positioned at whatever NSPoint you want:
class CenteredInParentWindow: NSWindow {
var parentMinX : CGFloat?
var parentMinY : CGFloat?
override func center() {
guard let parentMinX = parentMinX, let parentMinY = parentMinY else {
super.center()
return
}
self.setFrameOrigin(NSPoint(x: parentMinX, y: parentMinY))
}
}
2 - Set WindowController's window class to be the new NSWindow subclass, in Storyboard.
3- Instatiate the window controller and set the attributes of the subclassed window
let myWindowController = self.storyboard!.instantiateController(withIdentifier: "windowID") as! PlansWindowController
if let customWindow = myWindowController.window as? CenteredInParentWindow {
customWindow.parentMinX = NSApplication.shared.mainWindow?.frame.minX
customWindow.parentMinY = NSApplication.shared.mainWindow?.frame.minY
}
NSApp.runModal(for: myWindowController.window!)
}
You may need runModal method for making the window a modal one. Don't forget to include NSApp.stopModal() in the windowWillClose method which is available in NSWindowDelegate in your View controller

Open NSWindowController from NSMenu

I'm with a NSMenu in an agent application (without the icon in the dock). When a button from this menu is tapped, I want to show a generic NSWindowController.
My menu button action:
- (IBAction)menuButtonTapped:(id)sender {
MyWindowController *myWindow = [[MyWindowController alloc] initWithWindowNibName:#"MyWindowController"];
[myWindow showWindow:nil];
[[myWindow window] makeMainWindow];
}
But the window just "flashes" in the screen (it shows and disappears really fast).
Any solution?
The reason the window is showing up for a split second and then disappearing has to do with ARC and how you go about creating the instance of the window controller:
- (IBAction)menuButtonTapped:(id)sender {
MyWindowController *myWindow = [[MyWindowController alloc]
initWithWindowNibName:#"MyWindowController"];
[myWindow showWindow:nil];
[[myWindow window] makeMainWindow];
}
Under ARC, the myWindow instance will be valid for the scope where it is defined. In other words, after the last [[myWindow window] makeMainWindow]; line is reached and run, the window controller will be released and deallocated, and as a result, its window will be removed from the screen.
Generally speaking, for items or objects you create that you want to "stick around", you should define them as an instance variable with a strong property.
For example, your .h would look something like this:
#class MyWindowController;
#interface MDAppController : NSObject
#property (nonatomic, strong) MyWindowController *windowController;
#end
And the revised menuButtonTapped: method would look something like this:
- (IBAction)menuButtonTapped:(id)sender {
if (self.windowController == nil) {
self.windowController = [[MyWindowController alloc]
initWithWindowNibName:#"MyWindowController"];
}
[self.windowController showWindow:nil];
}
Use this:
[[myWindow window] makeKeyAndOrderFront:self];

Cocoa: Displaying an error after NSApp beginSheet results the main window hiding

I have broken this down into a very small project. Using the following code in the application delegate:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
TestingWindowController * testingWindowController = [[TestingWindowController alloc] initWithWindowNibName: #"TestingWindowController"];
// Begin our sheet
[NSApp beginSheet: testingWindowController.window
modalForWindow: self.window
modalDelegate: self
didEndSelector: #selector(windowDidEnd:returnCode:contextInfo:)
contextInfo: NULL];
}
- (void)windowDidEnd:(id)alert returnCode:(NSInteger)returnCode contextInfo:(id) contextInfo
{
// If the user did not accept, then we really don't care what else they did!
if (returnCode != NSOKButton) return;
// We have had an error. Display it.
[[NSApplication sharedApplication] presentError: nil
modalForWindow: self.window
delegate: nil
didPresentSelector: nil
contextInfo: NULL];
}
And the following action tied to button on the windows nib. (Note that the nib's window is also set to not be visible on launch).
- (IBAction) onClose: (id) sender
{
[[NSApplication sharedApplication] endSheet: self.window
returnCode: NSOKButton];
[self.window orderOut: nil];
} // End of onClose
What ends up happening is, once I the onClose runs, all of the windows disappear and I am left with nothing but the error dialog (the main window has disappeared).
Is there something wrong with my code? Why does my main window go away?
NOTE: I know that I am not passing an error to the presentError method. I purposely left this null as I only had a short time to write the sample code. Passing an actual error results in the same behaviour.
Sample project is available here.
Looks like you are still using the old api, try the new one
(deselect Always visible at launch for the UserLoginWindowController window)
- (IBAction)userButtonPressed:(id)sender {
UserLoginWindowController * wc = [UserLoginWindowController new];
// we keep a reference, so the WC doesn't deallocate
self.modalWindowController = wc;
[[self window] beginSheet:[wc window] completionHandler:^(NSModalResponse returnCode) {
self.modalWindowController = nil;
}];
}
in the UserLoginWindowController
- (IBAction)cancelButtonPressed:(id)sender {
[[[self window] sheetParent] endSheet:[self window] returnCode:NSModalResponseCancel];
}
You are using 2 methods to open your window, beginSheet:....., and runModalForWindow:. You only need one of those. If you want a sheet attached to your window, use the first method, if you want a stand alone window, use the second. Likewise, in your onClose method, you should use endSheet:returnCode: if you're closing a sheet (the argument for that method should be testingWindowController.window not self.window) , and stopModalWithCode: if you're closing a modal window, you shouldn't have both.

How show sheet loaded from xib? (MacOSx)

I have a xib file with only an NSPanel in it, I'm trying to show this panel as modal sheet (with beginSheet:modalForWindow:modalDelegate:didEndSelector:contextInfo:). The file's owner for this xib is a controller class "MyController" which has the IBOutlet to the NSPanel.
What I am looking for is something like:
...
MyController *controller = [[MyController alloc] init];
[NSApp beginSheet:controller.panel modalForWindow:[NSApp mainWindow] modalDelegate:controller didEndSelector:nil contextInfo:nil];
...
Question:
Must MyController inherit from NSWindowController or NSObject?. I tried NSWindowController and initWithWindowNibName: but the outlet to NSPanel always is nil.
Thanks
I resolve it. You must deactivate almost all the properties of the window object (in the IB) that you are using for the sheet. I add the following method to my controller to show the sheet:
- (void)showInWindow:(NSWindow *)mainWindow {
if (!panelSheet)
[NSBundle loadNibNamed:#"XibName" owner:self];
[NSApp beginSheet:panelSheet modalForWindow:mainWindow modalDelegate:nil didEndSelector:nil contextInfo:nil];
[NSApp runModalForWindow:panelSheet]; //This call blocks the execution until [NSApp stopModal] is called
[NSApp endSheet:panelSheet];
[panelSheet orderOut:self];
}
panelSheet is an IBOutlet to the sheet window.
Thanks Jon Hess and JWWalker for your help

makeKeyAndOrderFront only does the latter

I am trying to open one window from another using makeKeyAndOrderFront. The new window appears, but does not receive focus.
The code for the main window is:
#import "SecondWindowController.h"
#implementation FirstWindowController
-(IBAction)showSecondWindow:(id)sender
{
if (!secondWindowController)
secondWindowController = [[SecondWindowController alloc] init];
[[secondWindowController window] makeKeyAndOrderFront:self];
}
SecondWindowController is a NSWindowController, as follows:
#implementation SecondWindowController
-(id)init
{
if (![super initWithWindowNibName:#"SecondWindow"])
return nil;
return self;
}
I've also tried putting [secondWindowController showWindow:self] before the makeKeyAndOrderFront but it doesn't make a difference.
Did you make sure the window outlet for SecondWindowController is hooked up to the window in your NIB? The window could be displayed just by loading the NIB, even if the outlet is not hooked up.
Are you using a borderless window? If so you need to override canBecomeKeyWindow and return YES
Try this:
if (!secondWindowController)
secondWindowController = [[SecondWindowController alloc] init];
NSApplication *thisApp = [NSApplication sharedApplication];
[thisApp activateIgnoringOtherApps:YES];
[[secondWindowController window] makeKeyAndOrderFront:self];

Resources