我想使用线程更新进度条,如前所述here . 我正在努力实现这个结果:
- 进度条可见
- 进度条使用循环更新
- 进度条消失
这是我的代码:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
progressBar.hidden = NO;
for (NSInteger i = 1; i <= progressBar.maxValue; i += 20){
[NSThread sleepForTimeInterval:1.0];
dispatch_async(dispatch_get_main_queue(), ^{
[progressBar setDoubleValue:(double)i];
[progressBar displayIfNeeded];
});
}
progressBar.hidden = YES;
});
进度条在我的 ViewController.h int 中是这样定义的:
NSProgressIndicator *progressBar
问题是在循环结束时没有删除栏,我不知道progressBar.hidden = YES;
是否这样工作。
有人可以帮助我吗?代码片段会非常有用,特别是如果它后面有解释。
最佳答案
您所做的不好有两个原因。
首先,休眠线程不是正确的做法,除非您拥有该线程或确切知道它的职责。队列的线程由 GCD 所有,您应该将工作保持在队列级别,而不是较低级别。 (来自主队列的 block 将始终在主线程上运行。在某些情况下,尽管是有限的,但来自全局队列的 block 可能不会在后台线程上运行。*)
其次,您所问问题的原因:在后台线程中,您设置的hidden
是非主线程上的UI操作。这是不允许的,因为它会导致 UI 状态的同步问题。除了主线程外,在 Cocoa 中修改 View 的外观是不安全的。
这个问题你只答对了一半,为 setDoubleValue:
调用分派(dispatch)到主队列,但是设置 hidden
也需要在主线程上。
for 循环不是更新屏幕的好机制。我建议修改您的程序以重复调用一个方法。 NSTimer
是为做你正在做的事情而构建的。您应该可以轻松找到使用它的示例。
如果您想使用 GCD,我建议您改用单个 dispatch_after()
调用,在主队列延迟后重复运行一个 Block。像这样:
- (void)kickItOff
{
self.progressBar.hidden = NO;
[self updateProgress:0];
}
- (void)updateProgress:(double)progressValue
{
if( self.progressBar.maxValue <= progressValue ){
self.progressBar.hidden = YES;
return;
}
dispatch_time_t oneSecond = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
dispatch_after(oneSecond, dispatch_get_main_queue(), ^{
[self.progressBar setDoubleValue:progressValue];
[self updateProgress:progressValue + 20];
});
}
您可以通过调用 kickItOff
来启动更新周期。然后 updateProgress:
安排循环。这让主线程和运行循环继续畅通无阻地工作,同时确保您的代码以您想要的间隔运行。
*关于这一点的更深入:为了将 UI 实际绘制到屏幕上,主运行循环需要循环。如果主线程正在休眠,则不会发生这种情况:整个 UI 都被锁定以进行绘制和接受输入(并且主调度队列也未被处理)。
关于ios - Objective-C - 在循环后删除/隐藏 NSProgressIndicator,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41317341/