How to display and manage Cocoa's simple Application-Modal dialog box

I’m not sure if I’m doing everything right, or if I hacked it all.

I have a really simple test application (not document-based) that I created to learn how to work with modal dialog applications in Cocoa applications.

With the "TestModalDialog" application project, I have a simple MainMenu.xib with a default view and a "Show dialog" button, I added. I created a second XIB called TheDialog.xib, which has the Cancel and OK buttons. This xib has as its owner a class derived from NSWindowController called "TheDialogController"; window exit and delegate connected to the controller.

Selecting "Show dialog" on the main screen will launch the dialog. Selecting "Cancel" or "OK" will reject the dialog. Here is a 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 problems with modality - before I put userClickedCloseOrOk and tests, if the user clicked the close button (top left red dot), the dialog will close, but the modal session will still work.

I understand that I could just leave the button from the dialog box to start, but with that there I demonstrated a good way to catch this script or is there a better way? Or am I starting with something wrong that creates this problem for me?

Any advice would be appreciated.

NOTE. - The code from the original example is commented out and replaced with code based on the response. A new notification handler has also been added.

+6
source share
2 answers

In the okDialog: and closeDialog: call close instead of performClose: to avoid the window invoking the windowShouldClose: delegate method. Therefore, you do not need userClickedCloseOrOk .

I also think that you want to call stopModalWithCode: instead of stopModal , since in the application delegate you seem to be interested in the result. And you can call stopModal or stopModalWithCode: instead of abortModal , because you are always in runloop when you call it (interruption occurs when you are outside the modal runloop, for example, in another thread or runloop timer).

In windowShouldClose: you perform an action ( abortModal ) when you should only answer the question "should this close the window". The delegate method windowWillClose: is where you need to do the action if you need to.

Tables are useful when there is one window, and it tells the user that they can do nothing with it until they finish everything that is on the sheet. The windows of the mod application are applicable when you have several windows that the user cannot interact with until they finish everything that is in the modal window, or where there is an error that includes the entire application, but is not tied to the contents of one window. In its HIG, Apple is encouraged to avoid using application windows whenever possible.

+2
source

I really just struggled with the same problem and found this link:

Stop modality when closing a window (Cocoa)

+1
source

Source: https://habr.com/ru/post/908085/


All Articles