当我按下按钮时,如果我有一个无害的循环被触发:
- (IBAction)start:(id)sender{
calculatingProgressBar.progress = 0.0;
int j = 1000;
for (int i=0; i<j; i++) {
calculatingProgressBar.progress = (i/j);
}
}
ProgressView未更新。在其他语言中,有一个视图更新命令,您必须在这些情况下执行。什么是Cocoa等价物,或者你必须实现其他一些解决方法。
答案 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);