6

私は物事を正しい方法で行っているのか、それともすべてハッキングされているのか確信が持てません。

Cocoa アプリケーションでアプリケーション モーダル ダイアログを操作する方法を学習するために作成した、非常に単純なテスト アプリケーション (ドキュメント ベースではない) があります。

アプリケーション プロジェクト「TestModalDialog」では、デフォルト ビューと「ダイアログの表示」ボタンを備えた単純な MainMenu.xib を追加しました。「キャンセル」ボタンと「OK」ボタンを持つ TheDialog.xib という 2 番目の XIB を作成しました。その xib は、「TheDialogController」と呼ばれる NSWindowController から派生したクラスを所有者として持っています。ウィンドウのアウトレットとデリゲートはコントローラーに接続されています。

メイン ビューで [ダイアログを表示] を選択すると、ダイアログが起動します。「キャンセル」または「OK」を選択すると、ダイアログが閉じます。これは非常に単純なコードです:

//  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 とテストを入れる前に、ユーザーが閉じるボタン (左上の赤い点) を押すと、ダイアログは閉じますが、モーダル セッションはまだ実行されていました。

最初はダイアログの閉じるボタンをそのままにしておくことができると思いますが、それで、そのシナリオをキャッチする良い方法を示したのですか、それとももっと良い方法がありますか? それとも、そもそも何か間違ったことをしているのですか?それが私にその問題を引き起こしていますか?

アドバイスをいただければ幸いです。

- 元の例のコードはコメントアウトされ、回答に基づいたコードに置き換えられました。新しい通知ハンドラーも追加されました。

4

2 に答える 2

2

ウィンドウがデリゲート メソッドを呼び出すのを避ける代わりに、メソッドokDialog:closeDialog:メソッドを呼び出します。そうすれば必要ありません。closeperformClose:windowShouldClose:userClickedCloseOrOk

また、アプリのデリゲートでは結果に興味があるように見えるので、stopModalWithCode:代わりに呼び出したいと思います。stopModalまた、呼び出すときに常に実行ループにいるため、 stopModalorstopModalWithCode:の代わりに呼び出すことができabortModalます (中止は、別のスレッドやタイマーの実行ループのように、モーダル実行ループの外にいる場合です)。

windowShouldClose:アクション ( ) を実行しているときに、「このabortModalウィンドウを閉じる必要がありますか」という質問にのみ答える必要があります。デリゲート メソッドは、windowWillClose:必要に応じてアクションを実行する場所です。

シートは、ウィンドウが 1 つしかない場合に便利です。ユーザーは、シート内の内容をすべて完了するまで、そのウィンドウで何もできないことがわかります。アプリケーション モーダル ウィンドウは、モーダル ウィンドウ内の内容を完了するまでユーザーが操作できない複数のウィンドウがある場合、またはアプリケーション全体に関係するが 1 つのウィンドウのコンテンツに関連付けられていないエラーがある場合に便利です。Apple は HIG で、可能な限りアプリケーション モーダル ウィンドウの使用を避けることを提案しています。

于 2012-02-09T23:06:10.863 に答える
1

私は実際に同じ問題に苦しんでいて、このリンクを見つけました:

ウィンドウが閉じられたときにモーダルを停止する (Cocoa)

于 2012-02-09T21:37:26.710 に答える