3

我想使用线程更新进度条,如此处所示。我正在努力实现这个结果:

  1. 进度条变得可见
  2. 使用循环更新进度条
  3. 进度条消失

这是我的代码:

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;有效。

有人能帮我吗?代码片段将非常有用,特别是如果它后面有解释。

4

2 回答 2

5

你做的不好有两个原因。

首先,休眠线程不是正确的做法,除非您拥有该线程或确切知道它的职责。队列的线程归 GCD 所有,您应该将工作保持在队列级别,而不是较低级别。(主队列中的块将始终在主线程上运行。在某些情况下,尽管有限,但全局队列中的块可能不在后台线程上运行。*)

其次,以及您询问的问题的原因:在后台线程上,您的设置hidden是非主线程上的 UI 操作。这是不允许的,因为它会导致 UI 状态的同步问题。除了从主线程之外,在 Cocoa 中修改视图的外观是不安全的。

你的问题对了一半,调度到主队列进行setDoubleValue:调用,但设置hidden也需要在主线程上。

for 循环不是更新屏幕的好机制。我建议您修改您的程序以重复调用方法。AnNSTimer是为做你正在做的事情而构建的。你应该可以毫不费力地找到一个使用它的例子。

如果您想使用 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 被锁定以进行绘制和接受输入(并且主调度队列也没有被处理)。

于 2016-12-24T23:10:24.627 回答
2

你的代码是错误的。您正在从后台线程进行 UI 调用,这是不允许的。您需要在调用中包装更改进度条状态(包括将隐藏设置为 True 或 False)的调用dispatch_async(dispatch_get_main_queue()){}

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
            dispatch_async(dispatch_get_main_queue())^{
              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];
                    });
                }

            dispatch_async(dispatch_get_main_queue())^{
              progressBar.hidden = YES;
            }

        });

我猜这只是一个学习练习?

于 2016-12-24T22:51:28.303 回答