Cocoa Touch循环中的ProgressView不显示更新

时间:2012-05-15 21:27:24

标签: cocoa-touch uikit uiprogressview

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

- (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 个答案:

答案 0 :(得分:4)

问题是UI只在run(event)循环转动时定期更新。一个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,但基本思想保持不变。

如果您不熟悉并发(多线程)编程,那么您将获得适度的学习曲线以加快速度。我建议从Concurrency Programming Guide开始。

答案 1 :(得分: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个事件并安排它们分开运行一秒钟。然后返回(不运行它们)。

还有很多其他方法,例如NSTimer或GCD计时器。您还可以使用dispatch_async()将处理移至后台主题。 (见Conrad Shultz对此的回答)

答案 2 :(得分: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);
}

答案 3 :(得分:-1)

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