当我显示这样的NSAlert时,我立刻得到了回复:
int response; NSAlert *alert = [NSAlert alertWithMessageText:... ...]; response = [alert runModal];
问题是这是应用程序模式,我的应用程序是基于文档的.我使用工作表在当前文档的窗口中显示警报,如下所示:
int response; NSAlert *alert = [NSAlert alertWithMessageText:... ...]; [alert beginSheetModalForWindow:aWindow modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:&response]; //elsewhere - (void) alertDidEnd:(NSAlert *) alert returnCode:(int) returnCode contextInfo:(int *) contextInfo { *contextInfo = returnCode; }
唯一的问题是beginSheetModalForWindow:
直接返回,所以我无法可靠地询问用户问题并等待回复.如果我可以将任务分成两个区域,那么这不会是一件大事,但我不能.
我有一个循环来处理大约40个不同的对象(在树中).如果一个对象出现故障,我希望警报显示并询问用户是继续还是中止(继续在当前分支处理),但由于我的应用程序是基于文档的,因此Apple人机界面指南要求在警报为时使用工作表特定于文件.
如何显示警报表并等待响应?
我们创建了一个类别NSAlert
来同步运行警报,就像应用程序模式对话框一样:
NSInteger result; // Run the alert as a sheet on the main window result = [alert runModalSheet]; // Run the alert as a sheet on some other window result = [alert runModalSheetForWindow:window];
代码可以通过GitHub获得,并且下面发布了当前版本的完整性.
头文件NSAlert+SynchronousSheet.h
:
#import@interface NSAlert (SynchronousSheet) -(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow; -(NSInteger) runModalSheet; @end
实施文件NSAlert+SynchronousSheet.m
:
#import "NSAlert+SynchronousSheet.h" // Private methods -- use prefixes to avoid collisions with Apple's methods @interface NSAlert () -(IBAction) BE_stopSynchronousSheet:(id)sender; // hide sheet & stop modal -(void) BE_beginSheetModalForWindow:(NSWindow *)aWindow; @end @implementation NSAlert (SynchronousSheet) -(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow { // Set ourselves as the target for button clicks for (NSButton *button in [self buttons]) { [button setTarget:self]; [button setAction:@selector(BE_stopSynchronousSheet:)]; } // Bring up the sheet and wait until stopSynchronousSheet is triggered by a button click [self performSelectorOnMainThread:@selector(BE_beginSheetModalForWindow:) withObject:aWindow waitUntilDone:YES]; NSInteger modalCode = [NSApp runModalForWindow:[self window]]; // This is called only after stopSynchronousSheet is called (that is, // one of the buttons is clicked) [NSApp performSelectorOnMainThread:@selector(endSheet:) withObject:[self window] waitUntilDone:YES]; // Remove the sheet from the screen [[self window] performSelectorOnMainThread:@selector(orderOut:) withObject:self waitUntilDone:YES]; return modalCode; } -(NSInteger) runModalSheet { return [self runModalSheetForWindow:[NSApp mainWindow]]; } #pragma mark Private methods -(IBAction) BE_stopSynchronousSheet:(id)sender { // See which of the buttons was clicked NSUInteger clickedButtonIndex = [[self buttons] indexOfObject:sender]; // Be consistent with Apple's documentation (see NSAlert's addButtonWithTitle) so that // the fourth button is numbered NSAlertThirdButtonReturn + 1, and so on NSInteger modalCode = 0; if (clickedButtonIndex == NSAlertFirstButtonReturn) modalCode = NSAlertFirstButtonReturn; else if (clickedButtonIndex == NSAlertSecondButtonReturn) modalCode = NSAlertSecondButtonReturn; else if (clickedButtonIndex == NSAlertThirdButtonReturn) modalCode = NSAlertThirdButtonReturn; else modalCode = NSAlertThirdButtonReturn + (clickedButtonIndex - 2); [NSApp stopModalWithCode:modalCode]; } -(void) BE_beginSheetModalForWindow:(NSWindow *)aWindow { [self beginSheetModalForWindow:aWindow modalDelegate:nil didEndSelector:nil contextInfo:nil]; } @end
解决方案是打电话
[NSApp runModalForWindow:alert];
在beginSheetModalForWindow之后.此外,您需要实现捕获"对话框已关闭"操作的委托,并调用[NSApp stopModal]作为响应.
这里是解决这一问题的NSAlert类别(如通过与菲利普·弗雷德里克提出,由洛朗P改善:我用一个代码块,而不是委托的解决方案建议,因此它再次简化).
@implementation NSAlert (Cat) -(NSInteger) runModalSheetForWindow:(NSWindow *)aWindow { [self beginSheetModalForWindow:aWindow completionHandler:^(NSModalResponse returnCode) { [NSApp stopModalWithCode:returnCode]; } ]; NSInteger modalCode = [NSApp runModalForWindow:[self window]]; return modalCode; } -(NSInteger) runModalSheet { return [self runModalSheetForWindow:[NSApp mainWindow]]; } @end
万一有人来找这个(我做了),我解决了以下问题:
@interface AlertSync: NSObject { NSInteger returnCode; } - (id) initWithAlert: (NSAlert*) alert asSheetForWindow: (NSWindow*) window; - (NSInteger) run; @end @implementation AlertSync - (id) initWithAlert: (NSAlert*) alert asSheetForWindow: (NSWindow*) window { self = [super init]; [alert beginSheetModalForWindow: window modalDelegate: self didEndSelector: @selector(alertDidEnd:returnCode:) contextInfo: NULL]; return self; } - (NSInteger) run { [[NSApplication sharedApplication] run]; return returnCode; } - (void) alertDidEnd: (NSAlert*) alert returnCode: (NSInteger) aReturnCode { returnCode = aReturnCode; [[NSApplication sharedApplication] stopModal]; } @end
然后同步运行NSAlert就像:
AlertSync* sync = [[AlertSync alloc] initWithAlert: alert asSheetForWindow: window]; int returnCode = [sync run]; [sync release];
请注意,如所讨论的那样存在重新引发问题的可能性,因此如果这样做,请小心.