0

我目前正在通过实现我认为 Titanium Appcelerator Desktop SDK 中缺少的功能来学习(通过做)objective-c:一种使用自定义按钮文本进行模态对话框并可选择将它们显示为“工作表”的方法。

然而,当将 NSAlert 显示为“工作表”时,我创建警报的方法会立即返回,这就是我想要防止的,一切都很花哨且有效。

创建警报的方法返回一个 int(来自 NSAlert 的返回码)。

里面的代码基本上可以归结为:

int returnCode = -1;
if (displayAsSheet) {
    [alert beginSheetModalForWindow:nativeWindow modalDelegate:delegate didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil];

} else {
    returnCode = [alert runModal];
}

return returnCode;

modalDelegate 是一个实现所需的对象:

- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo;

现在它只是做一个返回代码的 NSLog。

所以我的问题是:

如何阻止我的方法返回,直到“工作表”被解除?

还是我以错误的方式解决这个问题?

4

5 回答 5

4

You must start a modal session for you sheet after showing it and stop the session after closing sheet.

Check this: https://github.com/incbee/NSAlert-SynchronousSheet, I think it will be helpfull.

于 2011-11-10T19:39:45.847 回答
1

你可以在之后使用它beginSheetModalForWindow:...

[[NSRunLoop mainRunLoop] runMode:NSModalPanelRunLoopMode beforeDate:[NSDate distantFuture]]

但是,它会使您的应用程序中的任何其他窗口都无法使用,直到工作表被关闭。最好不要挡住那些窗户。

于 2011-11-10T18:55:23.233 回答
1

my method that's creating the alert returns immediately

I believe that's because, as @Josh says, the sheet is running modal only relative to the window to which it is attached; it is not freezing the entire app. Therefore, as soon as beginSheetModal... executes, the rest of your method continues to run, concluding with return returnCode (here returning -1), without waiting for the user to respond to the alert.

The return code is a stand-in for which button on the alert panel the user ends up pushing (NSAlertFirstButtonReturn, NSAlertSecondButtonReturn, etc. -- they're listed at the end of the NSAlert class ref). You use it in your alertDidEnd method to act upon whichever button the user pushed to dismiss the alert. That's why the alertDidEnd selector includes the returnCode.

On the other hand, when you use the runModal method in your else block, you need to explicitly call alertDidEnd and feed it the number returned when the runModal method ends -- which is when the user dismisses the alert.

Here's a revised version of your code:

int returnCode = -1;
if (displayAsSheet) {
    [alert beginSheetModalForWindow:nativeWindow modalDelegate:delegate didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:nil];
    // The selector alertDidEnd has the returnCode int. The alert will then set that code to whatever the user chooses, and will send the altered int on to alertDidEnd.
} 
else {
    // Here, everything stops once runModal is called, until the user dismisses the alert, at which time the runModal method returns the int representing the button the user pushed, and you assign the return to your variable "returnCode."
    returnCode = [alert runModal];
    [self alertDidEnd:alert returnCode:returnCode contextInfo:nil];
}
// Omit the line returning the returnCode.

Then the alertDidEnd method does something like this:

- (void)alertDidEnd:(NSAlert *)alert returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo { 
switch (returnCode) {
    case NSAlertFirstButtonReturn:
        // Do whatever should happen when first button is pushed.
        break;
    case NSAlertSecondButtonReturn: 
        // Do whatever should happen when second button is pushed.
        break;
    default:
        break;
    }
    // Unfreeze things.
    [[NSApplication sharedApplication] stopModal];
}

By the way, there is a way of running a sheet and freezing the entire app, not just the window to which the sheet is attached, if that's what you want: modal tips

于 2011-11-10T19:48:48.143 回答
0

You're thinking about this slightly the wrong way. If your method were able to wait for the sheet to end, the app's event loop would be blocked and there would be no way for the user to interact with the UI. When you use runModal for the alert, a new run loop is created to handle the alert -- this is why nothing else can be done to the app. The point of the sheet option is to allow the user to do other things while the alert is displayed -- i.e., it expressly does not take over the event handling.

You could look into faking the sheet by attaching a child window.

于 2011-11-10T18:56:38.417 回答
0

You could try to set a boolean that freezes anything you want to freeze on your app (set freeze = YES) until the sheet is removed (set freeze = NO).
In the general case, you don't need to block a method : you just want some things not to happen until the user has made a choice.

For example, I have an app that uses gyroscope. It has some behaviour with, and some other behaviour without.
So I have a boolean that is used in any method that uses the gyro data to route the behaviour to the good one. My useGyro Boolean is NO when : the user is choosing what kind of hardware feature it want to enable or not, and when gyro is not available on the device.

The same thing with a mapView I have : when the user is aked by the system if it wants to be located, there is a moment where I freeze any behaviour using the user location. When he has made his choice, I change that bool value.

于 2011-11-10T18:57:49.280 回答