0

我注意到在我的程序中,当我调用 NSRunAlertPanel() 时,它等待返回(用户尚未单击按钮)NSTimer() 不会触发。它会定期触发,直到 NSRunAlertPanel() 行,但在用户单击对话框上的按钮之前不会再次调用。

有没有办法让计时器保持运行,即使对话框在屏幕上?

4

2 回答 2

1

警报面板正在启动自己的事件循环(通过-[NSApplication runModalForWindow:],与最初安排计时器的(可能是主)事件循环分开。这是为了防止用户在处理警报之前与应用程序的任何其他元素进行交互。

在我的脑海中,有两种方法可以让定时器在面板出现时仍然触发:运行主运行循环,并将定时器添加到面板的运行循环中。老实说,我不确定其中任何一个会如何解决。

首先,您可以很容易地获得应用程序的主运行循环:[NSRunLoop mainRunLoop],然后runUntilDate:在将来的短时间内(不到一秒)告诉它。这将需要您设置一个 ( while) 循环,让主运行循环和模态运行循环交替运行一小段时间。这里的问题是让主运行循环处于活动状态将允许处理输入,从而破坏面板的模式。

对于第二个,您只需参考计时器,然后执行[[NSRunLoop currentRunLoop] addTimer:forMode:]. 我在这里不确定的是计时器的触发日期将如何与重新添加到另一个循环进行交互,但您可以尝试一下。

希望我是个白痴并且遗漏了一些非常明显的东西,并且很快就会出现另一个答案以及正确的解决方案。

于 2012-08-14T23:26:41.630 回答
1

感谢 W'rkncacnter 的建议!下面是我的解决方案的净化版本。

将计时器放在屏幕上时,我使用:

timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateTimer) userInfo:nil repeats:YES];

这会每秒调用以下代码:

- (void) updateTimer {
    secondsRemaining--;
    if (secondsRemaining <= 0) {
        [timer invalidate];
        [[NSApplication sharedApplication] abortModal];
        [self finishStuff];
    }

    [self updateTimerText];
}

- (void) updateTimerText {
    NSInteger seconds = (int) secondsRemaining;
    NSInteger hours = seconds / (60 * 60);
    seconds -= hours * (60 * 60);
    NSInteger minutes = seconds / 60;
    seconds -= minutes * 60;

    [timerText setStringValue:[NSString stringWithFormat:@"%02ld:%02ld:%02ld", hours, minutes, seconds]];
}

注意“abortModal”代码。如果 NSRunAlertPanel() 在计时器用完时打开,它将被关闭。当我想显示对话框时,我运行以下代码:

[[NSRunLoop currentRunLoop] addTimer:timer
                             forMode:NSModalPanelRunLoopMode];
NSInteger buttonClicked = NSRunAlertPanel(@"Finish?", @"Are you sure you are done?", @"No", @"Yes", nil);

//"YES" clicked
if (buttonClicked == NSAlertAlternateReturn) {
    [timer invalidate];
    [self finishStuff];
}

请注意,我再次添加计时器,这次添加到不同的运行循环——用于模式窗口。计时器仍在原始运行循环中,如果对话框关闭则继续。

于 2012-08-15T20:30:04.513 回答