私は正しいことをやっているのか、それともすべてハッキングしているのか分かりません。ココアで簡単なアプリケーションモーダルダイアログを表示および管理する方法
私は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やテストに入れる前に、ユーザーが閉じるボタン(左上の赤い点)をヒットした場合、ダイアログは閉じだろうがモーダルセッションはまだ実行されていました。
私はちょうどダイアログから離れて閉じるボタンを残すことができたが、そこには、私がそのシナリオをキャッチするための良い方法を示しているか、それとも良い方法があるのだろうか?それとも、私が始めるのに間違ったことをしているのですか?それは私のために問題を作り出していますか?
アドバイスをいただければ幸いです。
注 - 元の例のコードはコメント化され、解答に基づいたコードに置き換えられました。また、新しい通知ハンドラも追加されました。
windowDidBecomeKey:またはwindowDidBecomeMainより良い方法はありますか?ウィンドウがモーダルセッションでいつ表示されたかを知るには?私はモーダルセッションが実際に始まるときを知りたいので、尋ねます。ウィンドウを作成/破棄するたびにwindowDidLoadを使用できますが、それは効率的ではありません。私は、モーダルセッションで表示しようとしているウィンドウに旗を送り、いずれかの通知からそれを管理することもできます - 再びハックのように感じます。あるいは、代わりにシートを使うべきですか? – GTAE86