objective-c - 简单的进度条不更新

标签 objective-c macos cocoa

我正在实现一个 Cocoa 应用程序,它只是一个简单的进度条,当我按下按钮时就会启动。

情况是:按下按钮时可以看到动画开始和停止,但进度条不会更新值。

我也尝试过这里提到的解决方案,但它不起作用:
How do I update a progress bar in Cocoa during a long running loop?

有人可以帮忙看看我的源代码中的问题出在哪里吗?

这是我的来源。

SimpleProgressBar.m

#import "SimpleProgressBar.h"

@implementation SimpleProgressBar
@synthesize progressBar;

int flag=0;

-(IBAction)startProgressBar:(id)sender{

    if(flag ==0){
        [self.progressBar startAnimation:sender];
        flag=1;
    }else{
        [self.progressBar stopAnimation:sender];
        flag=0;
    }
    [self.progressBar displayIfNeeded];
    [self.progressBar setDoubleValue:10.0];

    int i=0;

    for(i=0;i<100;i++){

        NSLog(@"progr: %f",(double)i);
        [self.progressBar setDoubleValue:(double)i];
        [self.progressBar setNeedsDisplay:YES];
    }


}

@end

SimpleProgressBar.h

#import < Foundation/Foundation.h >

@interface SimpleProgressBar : NSObject{

    __weak NSProgressIndicator *progressBar;

}

@property (weak) IBOutlet NSProgressIndicator *progressBar;

-(IBAction)startProgressBar:(id)sender;
@end

非常感谢您提供有用的答案。

更新:

这是我从解决方案中进行的移植,但它不起作用:

SimpleProgressBar.m

#import "SimpleProgressBar.h"

@implementation SimpleProgressBar
@synthesize progressBar;

int flag=0;

-(IBAction)startProgressBar:(id)sender{

    if(flag ==0){
        [self.progressBar startAnimation:sender];
        flag=1;
    }else{
        [self.progressBar stopAnimation:sender];
        flag=0;
    }
    [self.progressBar displayIfNeeded];
    [self.progressBar setDoubleValue:0.0];

    void(^progressBlock)(void);

    progressBlock = ^{

        [self.progressBar setDoubleValue:0.0];

        int i=0;

        for(i=0;i<100;i++){

            //double progr = (double) i / (double)100.0;
            double progr = (double) i;
            NSLog(@"progr: %f",progr);

             dispatch_async(dispatch_get_main_queue(),^{
             [self.progressBar setDoubleValue:progr];
             [self.progressBar setNeedsDisplay:YES];
             });

        }
    };

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_async(queue,progressBlock);

}

最佳答案

更新:

一些观察结果:

  1. 我发现如果你想观看 NSProgressIndicator 进度,你需要添加 sleepForTimeIntervalfor循环的迭代速度如此之快,以至于您不会看到进度指示器前进,而是会看到它很快就达到了最终状态。如果您插入 sleepForTimeInterval,您应该会看到它的进度:

    self.progressIndicator.minValue = 0.0;
    self.progressIndicator.maxValue = 5.0;
    [self.progressIndicator setIndeterminate:NO];
    
    self.progressIndicator.doubleValue = 0.001; // if you want to see it animate the first iteration, you need to start it at some small, non-zero value
    
    for (NSInteger i = 1; i <= self.progressIndicator.maxValue; i++)
    {
        [NSThread sleepForTimeInterval:1.0];
        [self.progressIndicator setDoubleValue:(double)i];
        [self.progressIndicator displayIfNeeded];
    }
    

    或者,如果您想在后台线程上执行 for 循环,并将更新分派(dispatch)回主队列:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        for (NSInteger i = 1; i <= self.progressIndicator.maxValue; i++)
        {
            [NSThread sleepForTimeInterval:1.0];
            dispatch_async(dispatch_get_main_queue(), ^{
                [self.progressIndicator setDoubleValue:(double)i];
                [self.progressIndicator displayIfNeeded];
            });
        }
    });
    
  2. 您正在使用 startAnimationstopAnimation,但根据 the documentation其中每一个“对于确定的进度指示器都没有任何作用”,因此这些调用似乎不适合这种情况。

<小时/>

下面我原来的答案是基于 Threads and Your User Interface 中的评论。在线程编程指南中,它说:

If your application has a graphical user interface, it is recommended that you receive user-related events and initiate interface updates from your application’s main thread. This approach helps avoid synchronization issues associated with handling user events and drawing window content. Some frameworks, such as Cocoa, generally require this behavior, but even for those that do not, keeping this behavior on the main thread has the advantage of simplifying the logic for managing your user interface.

但是下面的答案(错误地)是 iOS 答案,因此不适用。

原答案:

您的 for 循环正在主线程上运行,因此在您返回到运行循环之前,UI 更新不会出现。您还会如此快速地完成该循环,即使您正确地将其分派(dispatch)到后台队列,当您迭代循环时,您也不会遇到进度 View 发生变化的情况。

因此,在辅助线程上执行循环(例如通过 GCD 或操作队列),然后将 UI 更新分派(dispatch)回主线程,主线程现在可以自由地进行 UI 更新。因此,使用您的理论示例,您可以执行以下操作:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i = 0; i < 100; i++)
    {
        [NSThread sleepForTimeInterval:0.1];

        dispatch_async(dispatch_get_main_queue(), ^{
            [self.progressView setProgress: (CGFloat) (i + 1.0) / 100.0 animated:YES];
        });
    }
});

请注意,只有当您执行的操作速度足够慢才能看到进度 View 发生变化时,使用更新进度 View 的循环才有意义。在原来的示例中,您只是从 0 循环到 99,更新进度 View 。但这种情况发生得太快了,在这种情况下,进度观是没有意义的。这就是为什么我上面的示例不仅为循环使用后台队列,而且还添加了轻微的延迟(通过 sleepForTimeInterval)。

让我们考虑一下进度 View 的更实际的应用。例如,假设我有一个由 NSURL 对象组成的数组 urls,这些对象表示要从服务器下载的项目。然后我可能会做这样的事情:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    for (int i = 0; i < [urls count]; i++)
    {
        // perform synchronous network request (on main queue, you should only do asynchronous network requests, but on background queue, synchronous is fine, and in this case, needed)

        NSError *error = nil;
        NSURLResponse *response = nil;
        NSURLRequest *request = [NSURLRequest requestWithURL:urls[i]];
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:response error:error];

        // got it; now update my model and UI on the basis of what I just downloaded

        dispatch_async(dispatch_get_main_queue(), ^{
            [self.progressView setProgress: (CGFloat) (i + 1.0) / [array count] animated:YES];
            // do additional UI/model updates here
        });
    }
});

关于objective-c - 简单的进度条不更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19944268/

相关文章:

iphone - 如何识别模态视图 Controller 的类?

objective-c - 导航栏重新出现在欢迎屏幕上。如何永久隐藏它?

java - 如何使用 Swing 或 AWT 创建 OS X 原生模态对话框

c++ - 如何为 mac el capitan 安装 qt4?

objective-c - 如何用三角形弹出覆盖来呈现 UIView?

iphone - 如何为 UILabel 的移动设置动画

java - 在没有软件更新程序的 Mac 上安装 Java SE 6

objective-c - 我的 Cocoa 应用程序如何收到 NSScreen 分辨率更改的通知?

objective-c - 框架和动态库的主要区别是什么

iphone - 共享 View Controller 访问模型类