Как отобразить и управлять простым модально-прикладным диалогом в какао - PullRequest
6 голосов
/ 10 февраля 2012

Я не уверен, что делаю все правильно или у меня все взломано.

У меня очень простое тестовое приложение (не основанное на документах), которое я создал для обучения работес модальными диалоговыми окнами приложений в приложениях Какао.

В проекте приложения «TestModalDialog» у меня есть простой файл MainMenu.xib с представлением по умолчанию и кнопкой «Показать диалог», которую я добавил.Я создал второй XIB с именем TheDialog.xib, в котором есть кнопки «Отмена» и «ОК».Этот xib имеет в качестве владельца класс, производный от NSWindowController с именем «TheDialogController»;розетка и делегат подключены к контроллеру.

Выбор «Показать диалог» в главном окне откроет диалоговое окно.Выбор «Отмена» или «ОК» закроет диалог.Вот довольно простой код:

//  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  

У меня были проблемы с модальностью - перед тем, как я вставил userClickedCloseOrOk и проверил, если пользователь нажмет кнопку закрытия (верхняя левая красная точка), диалог закроетсяно модальный сеанс все еще продолжался.

Я понимаю, что могу просто оставить кнопку закрытия вне диалогового окна, чтобы начать с нее, но с ее помощью я продемонстрировал хороший способ уловить этот сценарий, илиЕсть ли способ лучше?Или я делаю что-то не так, чтобы начать, что создает для меня эту проблему?

Буду признателен за любой совет.

ПРИМЕЧАНИЕ - Код из исходного примера прокомментировани заменить на код, основанный на ответе.Также добавлен новый обработчик уведомлений.

Ответы [ 2 ]

2 голосов
/ 10 февраля 2012

В методах okDialog: и closeDialog: вызовите close вместо performClose:, чтобы избежать окна, вызывающего метод делегата windowShouldClose:.Таким образом, вам не нужно userClickedCloseOrOk.

Также я думаю, что вы хотите звонить stopModalWithCode: вместо stopModal, так как в делегате приложения вы, кажется, заинтересованы в результате.И вы можете вызвать stopModal или stopModalWithCode: вместо abortModal, потому что вы всегда находитесь в цикле выполнения, когда вызываете его (прерывание - для случаев, когда вы находитесь вне модального цикла запуска, как в другом потоке или цикле выполнения таймера).

В windowShouldClose: вы делаете действие (abortModal), когда вы должны отвечать только на вопрос «должно ли это окно закрыться».Метод делегата windowWillClose: - это то место, где вы должны выполнять действия, если вам это необходимо.

Листы полезны, когда есть одно окно, и он говорит пользователю, что он ничего не может с ним сделать, пока не завершит все, что находится вЛист.Модальные окна приложения полезны, когда у вас есть несколько окон, с которыми пользователь не может взаимодействовать, пока они не завершат то, что находится в модальном окне, или если есть ошибка, которая затрагивает все приложение, но не привязана к содержимому одного окна.В своем HIG Apple предлагает по возможности избегать использования модальных окон приложений.

1 голос
/ 10 февраля 2012

Я на самом деле только что боролся с той же проблемой и нашел эту ссылку:

Остановка модального режима при закрытии окна (Какао)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...