Programmatically close modal sheet Objective-C - objective-c

I have a window that needs to open another window as a model sheet. I have the following files:
MainMenu.xib (Main window for program obviously)
AppDelegate.m (This handles opening the Login.xib on applicationDidFinishLaunching)
Login.xib (The window that is used as the modal sheet, contains 2 textFields and a button)
LoginController.m (The processes the data that was filled out in the modal sheet)
AppDelegate.m:
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize loginScreen = _loginScreen;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Prompt user login credentials
[self activateLoginScreen];
}
-(IBAction)activateLoginScreen {
if (!_loginScreen)
[NSBundle loadNibNamed:#"Login" owner:self];
[NSApp beginSheet:self.loginScreen
modalForWindow:[[NSApp delegate] window]
modalDelegate:self
didEndSelector:NULL
contextInfo:NULL];
}
- (IBAction)closeLoginScreen:(id)sender {
[NSApp endSheet:self.loginScreen];
[self.loginScreen close];
self.loginScreen = nil;
}
My LoginController.m:
#import "LoginController.h"
#implementation LoginController
#synthesize txtMnemonic, txtPassword;
- (IBAction)btnLogin:(id)sender {
NSString *mnemonic = [txtMnemonic stringValue];
NSString *password = [txtPassword stringValue];
if ([mnemonic length] == 0 || [password length] == 0) {
NSLog(#"Please provide username and/or password.");
} else {
// Call web service here
}
}
#end
Also worth mentioning here that AppDelegate is the file owner of Login.xib.
My problem is that, I can open the modal window just fine when the app starts up. But how would I then close that window from within LoginController.m? And it can't be done with some button action. The button on that modal sheet is used to process the form. So after the form has been processed and everything has been verified, would I like to close the modal sheet. So basically call 'closeLoginScreen()' in AppDelegate.m from within LoginController.m

From within your LoginController you can close the sheet using
[NSApp endSheet:[self window] returnCode:NSCancelButton]; //... or NSOKButton
Although not necessary, the normal practice is to provide a delegate to perform cleanup!
-(IBAction)activateLoginScreen {
if (!_loginScreen)
[NSBundle loadNibNamed:#"Login" owner:self];
[NSApp beginSheet:self.loginScreen
modalForWindow:[[NSApp delegate] window]
modalDelegate:self
didEndSelector:#selector(mySheetWasClosed:returnCode:contextInfo:)
contextInfo:NULL];
}
And your delegate function
- (void)mySheetWasClosed:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
if (returnCode==NSOKButton)
{
// all validations have been made by the sheet
}
[sheet orderOut:self];
self.loginScreen = nil;
}

Related

Reopen a window and determine how it was closed

There is a button on my main window which opens another window to gather some user input. If the user completes the input, the window should close. If the user clicks the red button, I need to show an alert and let the window close. In either case I need to be able to reopen the input window.
I can open the window the first time, but not close it programmatically nor can I reopen it. I have read all the posts I can find, and didn't find one I could apply to my case. I think my problem is an outlet, and delegate.
The delegate for the window is Calibrate.
Calibrate.h
#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
.
.
#interface Calibrate : NSWindowController
.
.
#end
Calibrate.m
#import "Calibrate.h"
Calibrate *calibrate;
- (IBAction)showCalibratePanel:(id)sender
{
[calibrate showWindow:self];
}
- (void)handleMaxAngleChange:(NSNotification *)notification
{
///// last step in info gathering
NSString *s = [[notification userInfo]objectForKey:#"myMaxAngleKey"];
gotResponse = NO;
[calibrate close]; //////////// does not close.
}
#import "Calibrate.h"
#interface Calibrate ()
#end
#implementation Calibrate
.
.
- (id)init
{
self = [super initWithWindowNibName:#"Calibrate"];
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
}
-(BOOL)windowShouldClose:(NSNotification *)note
{ //// fires when red button clicked but alert not shown.
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:#"Incomplete calibration may cause ERC to not work correctly."];
[alert beginSheetModalForWindow:[NSApplication sharedApplication].mainWindow
modalDelegate:nil
didEndSelector:nil
contextInfo:nil];
return YES;
}
Use
[calibre.window close];
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSWindow_Class/#//apple_ref/occ/instm/NSWindow/close
To show the window use:
[calibrate showWindow:self];
as you did.

Binding a window created with the Cocoa Interface builder to a programmatically created one

I have an Objective C application which starts with loading a window created with Interface Builder:
//in main()
[NSApplication sharedApplication];
[NSBundle loadNibNamed:#"MainMenu" owner:NSApp];
[NSApp run];
In MainMenu.xib I have a window with a button. I want to create programmatically the second window when that button is pressed.
//in MainMenu.xib Controller.h
#class SecondWindowController;
#interface Controller: NSWindowController {
#private
SecondWindowController *sw;
}
- (IBAction)onButtonPress:(id)object;
#end
//in MainMenu.xib Controller.m
#import "SecondWindowController.h"
#implementation Controller
- (IBAction)onButtonPress:(id)object {
sw = [[SecondWindowController alloc] initWithWindowNibName:#"SecondWindow"];
[sw showWindow:self];
[[self window] orderOut:self];
}
#end
Where SecondWindowController inherits from NSWindowController. In SecondWindowController.h I have:
- (id)initWithWindow:(NSWindow*)window {
self = [super initWithWindow:window];
if (self) {
NSRect window_rect = { {custom_height1, custom_width1},
{custom_height2, custom_width2} };
NSWindow* secondWindow = [[NSWindow alloc]
initWithContentRect:window_rect
styleMask: ...
backing: NSBackingStoreBuffered
defer:NO];
}
return self;
}
And in the SecondWindow.xib I have nothing. When the button of the first window is pressed the first window disappears and the application closes. The reason I don't want to use the Interface builder for the second window is that I want to programmatically initialize it. Is this possible and if so what is the right way to accomplish this?
OK, I was initially confused with your use of initWithWindowNibName:#"SecondWindow" which will attempt to load the window from a NIB file, which you later mention you don't want to do.
Please use this to create your window:
- (IBAction)onButtonPress:(id)object {
if (!sw)
sw = [[SecondWindowController alloc] init];
[sw showWindow:self];
[[self window] orderOut:self];
}
Which will avoid creating multiple copies of the window controller, which you don't want (if you do then you'll need to store them in an array). Note the name sw is incorrect by convention; use either _sw or create setter/getter methods and use self.sw.
Initialize SecondWindowController like this:
- (id)init {
NSRect window_rect = NSMakeRect(custom_x, custom_y,
custom_width, custom_height);
NSWindow* secondWindow = [[NSWindow alloc]
initWithContentRect:window_rect
styleMask: ...
backing: NSBackingStoreBuffered
defer:NO];
self = [super initWithWindow:secondWindow];
if (self) {
// other stuff
}
return self;
}
Note: your variable names for origin/size of the new window were wrong; please review them.

Proper usage windows

I am working on my first Cocoa Mac OS X program and wondering the best approach to showing the windows.
I have my AppController / MainMenu.xib as the main launch window but have the MainMenu.xib window unchecked for Visible At Launch. I do this because on application load I am checking to see if they are logged in. If not I want to display the Login.xib window instead of the MainMenu.xib. Once logged in, I would open the MainMenu.xib window and close the LoginController I have this in the - (void)applicationDidFinishLaunching:(NSNotification *)aNotification method.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog(#"app delegate");
[[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Defaults" ofType:#"plist"]]];
BOOL didAuth = NO;
GTMOAuth2Authentication *auth = [GTMClasses authForService];
if (auth) {
didAuth = [GTMOAuth2WindowController authorizeFromKeychainForName:kKeychainName authentication:auth];
}
if (didAuth) {
[[DataClass sharedInstance] setIsSignedIn:YES];
NSLog(#"Already signed in %#", auth);
NSLog(#"Window: %#", self.window);
// SHOW MainMenu.xib here
} else {
NSLog(#"Not signed in %#", auth);
loginController = [[LoginController alloc] initWithWindowNibName:#"Login" owner:self];
[[loginController window] makeKeyAndOrderFront:self];
}
}
I see that AppController's awakeFromNib gets called before the applicationDidFinishLoadingWithOptions. Would it be best to put that code in my awakeFromNib?
If not, what is the best way to open the MainMenu.xib window from the AppDelegate?
If you have a better approach, what would it be?
PS: AppController is a subclass of NSObject so I don't have access to windowDidLoad or windowWillLoad
awakeFromNib is the first method to get executed.
You can also use alloc or init methods.
You can put your login authentication codes there, without any problem.
You must have seen the application life-cycle, how and when what methods get loaded.

How to Display and Manage a Simple Application-Modal Dialog in Cocoa

I am not sure I am doing things the right way, or if I have it all hacked up.
I have a really simple test application (not document-based) I created for learning how to work with application-modal dialogs in Cocoa applications.
With the application project "TestModalDialog", I have a simple MainMenu.xib with the default view and a button, "Show Dialog", I added. I created a second XIB called TheDialog.xib that has "Cancel" and "OK" buttons. That xib has as its owner a class derived from NSWindowController called "TheDialogController"; the window outlet and delegate are connected to the controller.
Selection of "Show Dialog" on the main view will launch the dialog. Selection of "Cancel" or "OK" will dismiss the dialog. Here is the pretty simple code:
// TestModalDialogAppDelegate.h
// TestModalDialog
#import <Cocoa/Cocoa.h>
#class TheDialogController;
#interface TestModalDialogAppDelegate : NSObject <NSApplicationDelegate>
{
NSWindow *window;
TheDialogController* theDialogController;
}
#property (assign) IBOutlet NSWindow *window;
- (IBAction)showDialog:(id)sender;
#end
// TestModalDialogAppDelegate.m
// TestModalDialog
#import "TestModalDialogAppDelegate.h"
#import "TheDialogController.h"
#implementation TestModalDialogAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
theDialogController= [[TheDialogController alloc] init];
}
- (void)dealloc
{
if(nil != theDialogController)
[theDialogController release];
[super dealloc];
}
- (IBAction)showDialog:(id)sender
{
if(nil == theDialogController)
{
NSAlert* alert= [NSAlert alertWithMessageText:#"Dialog Error" defaultButton:nil alternateButton:nil otherButton:nil informativeTextWithFormat:#"The dialog controller was not allocated."];
[alert runModal];
return;
}
NSInteger result= [NSApp runModalForWindow:[theDialogController window]];
// Do something with result....
}
#end
// TheDialogController.h
// TestModalDialog
#import <Cocoa/Cocoa.h>
#interface TheDialogController : NSWindowController
{
// BOOL userClickedCloseOrOk; // Removed based on answer.
// Should declare a common define - just being lazy.
NSInteger userClickedOk; // Added based on answer.
UInt16 timesShown;
}
- (IBAction)showWindow:(id)sender;
- (IBAction)closeDialog:(id)sender;
- (IBAction)okDialog:(id)sender;
//- (BOOL)windowShouldClose:(id)sender; // Removed based on answer.
- (void)windowWillClose:(NSNotification*)notification; // Added based on answer.
- (void)windowDidBecomeKey:(NSNotification*)notification; // To set title when modal.
#end
// TheDialogController.m
// TestModalDialog
#import "TheDialogController.h"
#implementation TheDialogController
- (id)init
{
self = [super initWithWindowNibName:#"TheDialog"];
userClickedOk= 0; // Added based on answer.
// userClickedCloseOrOk= FALSE; // Removed based on answer.
return self;
}
-(void)dealloc
{
// Do member cleanup if needed.
[super dealloc];
}
- (void)windowDidLoad
{
[super windowDidLoad];
// Initialize as needed....
[[self window] center]; // Center the window.
}
// Does not show with runModalForWindow.
- (IBAction)showWindow:(id)sender
{
// Just playing with the window title....
++timesShown;
NSString* newTitle= [NSString stringWithFormat:#"Shown %d Times", timesShown];
[[self window] setTitle:newTitle];
return [super showWindow:sender];
}
// This method no longer used for this solution based on the answer.
//- (BOOL)windowShouldClose:(id)sender
//{
// if(!userClickedCloseOrOk) // The user did not click one of our buttons.
// [NSApp abortModal];
// else
// userClickedCloseOrOk= FALSE; // Clear for next time.
//
// return TRUE;
//}
// Added based on answer.
- (void)windowWillClose:(NSNotification*)notification
{
[NSApp stopModalWithCode:userClickedOk];
userClickedOk= 0; // Reset for next time.
}
// Note - the title will update every time the window becomes key. To do the
// update only once per modal session, a flag can be added. There might be a better
// notification to catch.
- (void)windowDidBecomeKey:(NSNotification*)notification
{
++timesShown;
NSString* newTitle= [NSString stringWithFormat:#"Shown %d Times", timesShown];
[[self window] setTitle:newTitle];
}
- (IBAction)closeDialog:(id)sender
{
//userClickedCloseOrOk= TRUE; // Removed based on answer.
//[NSApp abortModal]; // Removed based on answer.
//[[self window] performClose:self]; // Removed based on answer.
[[self window] close]; // Know we want to close - based on answer.
}
- (IBAction)okDialog:(id)sender
{
userClickedOk= 1; // Added based on answer.
//userClickedCloseOrOk= TRUE; // Removed based on answer.
//[NSApp stopModal]; // Removed based on answer.
//[[self window] performClose:self]; // Removed based on answer.
[[self window] close]; // Know we want to close - based on answer.
}
#end
I had trouble with the modality - before I put in userClickedCloseOrOk and tests, if the user hit the close button (upper-left red dot), the dialog would close but the modal session was still running.
I realize I could just leave the close-button off the dialog to start with, but with it there, is what I have demonstrated a good way to catch that scenario, or is there a better way? Or, am I doing something wrong to start with, which creates that problem for me?
Any advice would be appreciated.
NOTE - Code from the original example commented out and replaced with code based on the answer. Also added a new notification handler.
In the okDialog: and closeDialog: methods call close instead of performClose: to avoid the window calling the windowShouldClose: delegate method. That way you don't need userClickedCloseOrOk.
Also I think you want to be calling stopModalWithCode: instead of stopModal since in the app delegate you seem to be interested in the result. And you can call stopModal or stopModalWithCode: instead of abortModal because you are always in the runloop when you call it (abort is for when you are outside of the modal runloop, like in another thread or timer's runloop).
In windowShouldClose: you are doing an action (abortModal) when you should only be answering the question "should this window close". The windowWillClose: delegate method is where you should do actions if you need to.
Sheets are useful when there is one window and it tells the user they can't do anything with it until they complete whatever is in the sheet. Application-modal windows are useful when you have multiple windows that the user can't interact with until they complete whatever is in the modal window or where there is an error that involves the whole application but is not tied to the content of one window. In their HIG Apple suggests avoiding the use of Application-modal windows whenever possible.
I have actually just been struggling with the same problem and found this link :
Stopping modal when window is closed (Cocoa)

How do you implement the Method makeKeyAndOrderFront:?

I am making a new window open and would like to implement the method makeKeyAndOrderFront: for the window, i was wondering what code i would need to enter to do this.
Here is some of the code I've already got to open the window:
File 1 (The First Controller)
#import "PreferenceController.h"
#implementation PreferenceController
- (id)init
{
if (![super initWithWindowNibName:#"Preferences"])
return nil;
return self;
}
- (void)windowDidLoad
{
NSLog(#"Nib file is loaded");
}
File 2 (The Action Opening The Window)
#import "Prefernces_Delegate.h"
#import "PreferenceController.h"
#implementation Prefernces_Delegate
- (IBAction)showPreferencePanel:(id)sender
{
// Is preferenceController nil?
if (!preferenceController) {
preferenceController = [[PreferenceController alloc] init];
}
NSLog(#"showing %#", preferenceController);
[preferenceController showWindow:self];
}
The reason I am trying to do this is it has been suggested by a friend to solve a window opening problem.
You don't want to implement -makeKeyAndOrderFront:, you want to call it on your window in order to bring it to front and make it the key window. What does your showWindow: method do?
Somewhere after [preferenceController showWindow:self];:
[self.window makeKeyAndOrderFront:self];
or did you mean add a method to the controller?
// you should use a different method name, cause it's not the
// controller that is made key and ordered front.
- (void)makeKeyAndOrderFront:(id)IBAction {
[self.window makeKeyAndOrderFront:self];
}
Message makeKeyAndOrderFront is sent only after initiating the main event loop [NSApp run]. You may try to send the message from the main view by implementing the method viewWillDraw:
- (void)viewWillDraw
{
[window makeKeyAndOrderFront: nil];
}