No estoy seguro de estar haciendo las cosas bien o si lo tengo todo pirateado.Cómo mostrar y administrar un cuadro de diálogo simple de aplicación-modal en Cocoa
Tengo una aplicación de prueba realmente simple (no basada en documentos) que he creado para aprender a trabajar con diálogos de aplicación-modal en aplicaciones Cocoa.
Con el proyecto de aplicación "TestModalDialog", tengo un MainMenu.xib simple con la vista por defecto y un botón, "Mostrar cuadro de diálogo", agregué. Creé un segundo XIB llamado TheDialog.xib que tiene los botones "Cancelar" y "Aceptar". Ese xib tiene como propietario una clase derivada de NSWindowController llamada "TheDialogController"; la salida de la ventana y el delegado están conectados al controlador.
La selección de "Mostrar cuadro de diálogo" en la vista principal iniciará el cuadro de diálogo. La selección de "Cancelar" o "Aceptar" cancelará el diálogo. Aquí está el código bastante simple:
// 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
tuve problemas con la modalidad - antes de poner en userClickedCloseOrOk y las pruebas, si el usuario pulsa el botón de cierre (punto rojo superior izquierda), el cuadro de diálogo se cerraría, pero el la sesión modal aún se estaba ejecutando.
Me di cuenta de que podía dejar el botón de cerrar el cuadro de diálogo para comenzar, pero con eso, ¿qué es lo que he demostrado que es una buena forma de captar ese escenario, o hay una forma mejor? O, ¿estoy haciendo algo mal para empezar, que me crea ese problema?
Cualquier consejo sería apreciado.
NOTA - Código del ejemplo original comentado y reemplazado por código basado en la respuesta. También se agregó un nuevo controlador de notificaciones.
¿Hay una manera mejor que windowDidBecomeKey: o windowDidBecomeMain: saber cuándo se ha mostrado la ventana en una sesión modal? Lo pregunto porque me gustaría saber cuándo comienza realmente la sesión modal. Podría crear/destruir la ventana cada vez, y luego usar windowDidLoad, pero eso no parece muy eficiente. También podría enviar una bandera para indicarle a la ventana que está a punto de mostrar en una sesión modal, y administrarla desde cualquiera de las notificaciones; de nuevo, se siente como un truco. O, ¿debería usar una hoja en su lugar? – GTAE86