Objective C - 循环后删除/隐藏 NSProgressIndicator

Posted

技术标签:

【中文标题】Objective C - 循环后删除/隐藏 NSProgressIndicator【英文标题】:Objective C - Remove/Hide NSProgressIndicator after loop 【发布时间】:2016-12-24 22:38:19 【问题描述】:

我想使用线程更新进度条,如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

问题是循环结束时bar没有去掉,不知道progressBar.hidden = YES;是不是这样工作的。

有人可以帮助我吗?一个代码 sn-p 将非常有用,特别是如果它后面有解释。

【问题讨论】:

主要问题是 progressBar.hidden 没有像其他进度条更新一样被分派到主队列。 【参考方案1】:

你做的不好有两个原因。

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

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

您的问题对了一半,为setDoubleValue: 调用分派到主队列,但设置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 被锁定以进行绘制和接受输入(并且主调度队列也没有被处理)。

【讨论】:

乔希的回答比我的更好、更完整。我只是回答了被问到的问题。他详细介绍了一种更好的做事方式。 (已投票) 嗯,你说得对,@Rob,除了dispatch_sync() 的情况外,我对此没有信心。我会缓和措辞;谢谢! 谢谢你的解释;) 很高兴我能帮上忙,@Ema.jar【参考方案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;
            

        );

我认为这只是一个学习练习?

【讨论】:

感谢您的努力:+1

以上是关于Objective C - 循环后删除/隐藏 NSProgressIndicator的主要内容,如果未能解决你的问题,请参考以下文章

Objective C如何在从第一个VC移动到第二个时停止无限循环

在Objective C的IQKeyboardManager中隐藏下一个上一个按钮

Objective C 到 SwiftUI 隐藏导航栏

根据objective c中得到的json隐藏行和节

js 代码判断文字后隐藏某div代码

Objective-C:如何在课堂外隐藏课堂成员?