0

如果我有一个无害的循环,当我按下一个按钮时会被触发:

- (IBAction)start:(id)sender{
    calculatingProgressBar.progress = 0.0;  
    int j = 1000;
    for (int i=0; i<j; i++) {
        calculatingProgressBar.progress = (i/j);
    }
}

ProgressView 未更新。在其他语言中,您必须在这些情况下执行视图更新命令。什么是 Cocoa 等价物,或者您是否必须采取其他一些解决方法。

4

4 回答 4

4

问题是 UI 仅在运行(事件)循环转动时定期更新。for()我假设在主线程上运行的循环将阻塞运行循环直到完成。这不仅意味着进度条不会更新(或者,更确切地说,最后会直接跳到 100%),而且您可能会冻结 UI 一段时间。(我假设,由于您正在显示进度条,这可能需要一段时间。)

对于任何冗长的操作,您想要做的是在单独的队列/线程上完成工作,并定期调用主线程上的 UI 更新。例如,您可以尝试类似(警告:在浏览器中键入):

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
    int j = 1000;
    for (int i=0; i<j; i++) {
        // Do your presumably useful work, not just looping for fun :-)
        dispatch_async(dispatch_get_main_queue(), ^{
            // UI updates always come from the main queue!
            calculatingProgressBar.progress = (i/j);
        });
    }
});

此示例使用 GCD(Grand Central Dispatch)。您还可以查看更高级别的构造,例如NSOperationQueue,但基本思想保持不变。

如果您不熟悉并发(多线程)编程,那么您将有一个适度的学习曲线来加快速度。我建议从并发编程指南开始。

于 2012-05-15T22:45:20.037 回答
1

当您在主线程上运行代码时,UI 无法自行更新。这是有意的,并且大大提高了性能。这意味着在start返回之前不会对 UI 进行任何更新。你更新progress了很多次,然后又回来了,所以它只画了一次。

您将需要使用计时器或 GCD 队列来实现您所说的。这在很大程度上取决于其余代码的外观,但这是一种方法,例如,如果您希望它运行超过 60 秒:

- (IBAction)start:(id)sender{
  calculatingProgressBar.progress = 0.0;  
  int j = 60;

  dispatch_time_t now = dispatch_time(DISPATCH_TIME_NOW, 0);
  for (int i=0; i<j; i++) {
    dispatch_after(dispatch_time(now, i * NSEC_PER_SEC),
                   dispatch_get_current_queue(),
                   ^{
                     calculatingProgressBar.progress = (i/j);
                    });
    }
}

它的作用是设置 60 个事件并安排它们运行一秒钟。然后它返回(还没有运行它们)。

还有很多其他方法,例如NSTimerGCD 计时器。您还可以使用dispatch_async(). (请参阅康拉德舒尔茨的回答)

于 2012-05-15T22:46:12.397 回答
0

我认为一个更优雅(和通用)的解决方案是:在你的代表中,有这个:

maxNumber = 1000; // define this somewhere, just for convenience
...
[self performSelectorInBackground:@selector(doLoop:) withObject:[NSNumber numberWithInt:maxNumber]];

有这样的方法:

- (void)doLoop:(NSNumber *)myMaxNumber { // this runs on the background
  calculatingProgressBar.progress = 0.0;  
  NSNumber *j = myMaxNumber;
  for (int i=0; i<j; i++) {
    [self performSelectorOnMainThread:@selector(updateProgress:) withObject:[NSNumber numberWithInt:i] waitUntilDone:NO];
  }
  // here you could call a method in the main thread, just to signify that your loop is done.
  // [self performSelectorOnMainThread:@selector(loopDone:) withObject:nil waitUntilDone:NO];
}

并且更新在这里完成:

-(void)updateProgress:(NSNumber *)myNumber {
  calculatingProgressBar.progress = ([myNumber intValue]/maxNumber);
}
于 2012-12-10T19:03:44.520 回答
-1

假设calculatingProgressBar 是一个IBOutlet,那么您可能只需要以符合KVO 的方式设置新值,即使用setter:self.calculatingProgressBar.progress = (i/j);

于 2012-05-15T22:35:12.463 回答