2012-02-09 17 views
6

私は正しいことをやっているのか、それともすべてハッキングしているのか分かりません。ココアで簡単なアプリケーションモーダルダイアログを表示および管理する方法

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

"TestModalDialog"というアプリケーションプロジェクトでは、デフォルトのビューとボタン「Show Dialog」が追加されたシンプルなMainMenu.xibを持っています。 TheDialog.xibという名前の2番目のXIBを作成しました。これには「キャンセル」と「OK」ボタンがあります。そのxibは "TheDialogController"という名前のNSWindowControllerから派生したクラスをオーナーとして持っています。ウィンドウのアウトレットとデリゲートがコントローラに接続されます。

メインビューで[Show Dialog]を選択すると、ダイアログが表示されます。 「キャンセル」または「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やテストに入れる前に、ユーザーが閉じるボタン(左上の赤い点)をヒットした場合、ダイアログは閉じだろうがモーダルセッションはまだ実行されていました。

私はちょうどダイアログから離れて閉じるボタンを残すことができたが、そこには、私がそのシナリオをキャッチするための良い方法を示しているか、それとも良い方法があるのだろうか?それとも、私が始めるのに間違ったことをしているのですか?それは私のために問題を作り出していますか?

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

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

+0

windowDidBecomeKey:またはwindowDidBecomeMainより良い方法はありますか?ウィンドウがモーダルセッションでいつ表示されたかを知るには?私はモーダルセッションが実際に始まるときを知りたいので、尋ねます。ウィンドウを作成/破棄するたびにwindowDidLoadを使用できますが、それは効率的ではありません。私は、モーダルセッションで表示しようとしているウィンドウに旗を送り、いずれかの通知からそれを管理することもできます - 再びハックのように感じます。あるいは、代わりにシートを使うべきですか? – GTAE86

答えて

2

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

また、stopModalの代わりにstopModalWithCode:を呼び出したいと思うのですが、アプリケーションデリゲートでは結果に興味があるようです。また、abortModalの代わりにstopModalまたはstopModalWithCode:を呼び出すことができます。なぜなら、呼び出すときに常にランループにいるからです(中断は、別のスレッドやタイマーのrunloopのようにモーダルランループの外にあるためです)。

windowShouldClose:「should this this window close」という質問に答える必要がある場合は、アクション(abortModal)を実行しています。 windowWillClose:代理人メソッドは、必要に応じてアクションを実行する場所です。

シートは1つのウィンドウがある場合に便利です。シートに何が入っていても完了するまで、何もできないことをユーザーに通知します。アプリケーションモーダルウィンドウは、モーダルウィンドウ内の何かを完了するまで、またはアプリケーション全体に関わらず1つのウィンドウの内容に結び付けられていないエラーがあるまで、ユーザーが対話できない複数のウィンドウがある場合に便利です。彼らのHIGでは、可能であればApplication-modalウィンドウの使用を避けることを提案しています。

+0

performCloseからcloseへの変更は実際に何も変更しませんでした。私がwindowShouldCloseでuserClickedCloseOrOkをチェックした理由は、ユーザーがフレームの閉じるボタン(標準の閉じるボタン)を押したときにキャッチすることです。私がそれをキャッチすると、私はabortModalを呼び出すことができます - あるいはstopModalを呼び出すこともできます。それ以外の場合、モーダルセッションは終了せず、アプリケーションを強制終了する必要があります。私がやっていることはハックのように思えますし、私がモーダルダイアログを「正しい」方法で管理していれば、それは必要ないでしょう。 abortModalとstopModalの解明に感謝します。 – GTAE86

+0

Aaahhh私はあなたが今何を意味しているのかもっと見ていると思う - モーダルなものをwindowWillCloseに移動すると、どこにも必要ないだろう。次に、okDialog:またはcloseDialog:でコードを適切に設定し、stopModalWithCode:を呼び出します。ニース。 – GTAE86

1

私は実際には同じ問題で苦労し、このリンクを発見されている:okDialog:closeDialog:方法で

Stopping modal when window is closed (Cocoa)

+0

「最初は正しいことをしていますか?最初はシートやモーダルダイアログを使うべきかどうかで苦労しました。私は簡単に良い答えを見つけることができませんでした。私は、物事をまったく動作させる方法について、一日苦労しました。それは、窓のコンセントをコントローラに接続する方法と、本当に尋ねる質問を考え出すことに至りました。 – GTAE86

+0

私はシートを使うことも考えましたが、私のウィンドウは移動可能で、親ウィンドウには付けられませんでした。シートでOS Xの露光を使用すると、モーダルウィンドウは余分なものとして表示されません。私をオタクと呼びますが、私はそれを望んでいませんでした:) – guitarflow

関連する問題