等待[NSAlert beginSheetModalForWindow:...];
-
03-07-2019 - |
题
当我显示的一个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个不同的对象(那是在树)。如果一个目的失败了,我想该警报,以显示和询问用户是否继续或中止(继续处理目前的分支),但由于我的应用程序的文件的基础,苹果人口的准则,规定使用片当警觉的是,具体到一份文件。
我如何可以显示该警报纸和等待反应?
解决方案
不幸的是,没有多少你可以在这里做。你基本上要做出决定:重新设计应用程序,以便它可以处理的对象在一个异的方式或使用的非获得批准,废弃的建筑提出应用程序模式的警报。
不知道任何有关的信息实际设计和如何处理这些对象,它很难得到任何进一步的信息。关掉我的头顶,虽然,一对夫妇的想法可能是:
- 进程的目的在另外一个线程,与主要线穿过某种的运行环信号或排队。如果窗口的对象树被中断,信号的主要线,它被中断,等待信号从主线的相关信息做什么(继续这个分支机构或中止).主要的线,然后提出的文档模式窗口和信号的过程后,用户选择什么做。
这可能是真的过于复杂为你需要什么,但是。在这种情况下,我的建议是只是随弃使用,但这真的取决于用户要求。
其他提示
我们在 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 <Cocoa/Cocoa.h>
@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类别(正如Philipp所建议的解决方案由Frederick提出并由Laurent 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];
请注意,如所讨论的那样存在重新引发问题的可能性,因此如果这样做,请小心。
这是我的回答:
创建一个全局类变量'NSInteger alertReturnStatus'
- (void)alertDidEndSheet:(NSWindow *)sheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
{
[[sheet window] orderOut:self];
// make the returnCode publicly available after closing the sheet
alertReturnStatus = returnCode;
}
- (BOOL)testSomething
{
if(2 != 3) {
// Init the return value
alertReturnStatus = -1;
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
[alert addButtonWithTitle:@"OK"];
[alert addButtonWithTitle:@"Cancel"];
[alert setMessageText:NSLocalizedString(@"Warning", @"warning")];
[alert setInformativeText:@"Press OK for OK"];
[alert setAlertStyle:NSWarningAlertStyle];
[alert setShowsHelp:NO];
[alert setShowsSuppressionButton:NO];
[alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(alertDidEndSheet:returnCode:contextInfo:) contextInfo:nil];
// wait for the sheet
NSModalSession session = [NSApp beginModalSessionForWindow:[alert window]];
for (;;) {
// alertReturnStatus will be set in alertDidEndSheet:returnCode:contextInfo:
if(alertReturnStatus != -1)
break;
// Execute code on DefaultRunLoop
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
// Break the run loop if sheet was closed
if ([NSApp runModalSession:session] != NSRunContinuesResponse
|| ![[alert window] isVisible])
break;
// Execute code on DefaultRunLoop
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
beforeDate:[NSDate distantFuture]];
}
[NSApp endModalSession:session];
[NSApp endSheet:[alert window]];
// Check the returnCode by using the global variable alertReturnStatus
if(alertReturnStatus == NSAlertFirstButtonReturn) {
return YES;
}
return NO;
}
return YES;
}
希望它会有所帮助, 干杯 --Hans
这是上面Laurent等人的版本,为Xcode 6.4(截至今天的最新工作版本)翻译成Swift 1.2并在我的应用程序中进行了测试。感谢所有为此工作做出贡献的人! Apple的标准文档没有提供关于如何处理这个问题的线索,至少在任何我找不到的地方都没有。
我仍然有一个谜:为什么我必须在最后一个函数中使用双感叹号。 NSApplication.mainWindow应该只是一个可选的NSWindow(NSWindow?),对吧?但是编译器给出了错误,直到我使用了第二个'!'。
extension NSAlert {
func runModalSheetForWindow( aWindow: NSWindow ) -> Int {
self.beginSheetModalForWindow(aWindow) { returnCode in
NSApp.stopModalWithCode(returnCode)
}
let modalCode = NSApp.runModalForWindow(self.window as! NSWindow)
return modalCode
}
func runModalSheet() -> Int {
// Swift 1.2 gives the following error if only using one '!' below:
// Value of optional type 'NSWindow?' not unwrapped; did you mean to use '!' or '?'?
return runModalSheetForWindow(NSApp.mainWindow!!)
}
}
与Windows不同,我认为没有办法阻止模态对话框。输入(例如用户点击按钮)将在主线程上处理,因此无法阻止。
对于您的任务,您必须将消息传递到堆栈中,然后从中断处继续。
当一个对象失败时,停止处理树中的对象,记下哪个对象失败(假设有一个订单,你可以从你离开的地方拿起),并抛出工作表。当用户取消工作表时,让 didEndSelector:
方法从它停止的对象再次开始处理,取决于 returnCode
。
- (bool) windowShouldClose: (id) sender
{// printf("windowShouldClose..........\n");
NSAlert *alert=[[NSAlert alloc ]init];
[alert setMessageText:@"save file before closing?"];
[alert setInformativeText:@"voorkom verlies van laatste wijzigingen"];
[alert addButtonWithTitle:@"save"];
[alert addButtonWithTitle:@"Quit"];
[alert addButtonWithTitle:@"cancel"];
[alert beginSheetModalForWindow: _window modalDelegate: self
didEndSelector: @selector(alertDidEnd: returnCode: contextInfo:)
contextInfo: nil];
return false;
}
您可以使用 dispatch_group_wait(group,DISPATCH_TIME_FOREVER);
:
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:@"alertMessage"];
[alert addButtonWithTitle:@"Cancel"];
[alert addButtonWithTitle:@"Ok"];
dispatch_async(dispatch_get_main_queue(), ^{
[alert beginSheetModalForWindow:progressController.window completionHandler:^(NSModalResponse returnCode) {
if (returnCode == NSAlertSecondButtonReturn) {
// do something when the user clicks Ok
} else {
// do something when the user clicks Cancel
}
dispatch_group_leave(group);
}];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//you can continue your code here
希望有所帮助。