简体   繁体   English

简单进度条未更新

[英]Simple progress bar is not updating

I am implementing a Cocoa Application which is just a simple progress bar that starts when I press a button. 我正在实现一个Cocoa应用程序,它只是一个简单的进度栏,当我按下按钮时启动。

The situation is: I can see Animation is Start and Stop when I press the button, but the progress bar will not update the value. 情况是:按下按钮后,我可以看到“动画是开始和停止”,但是进度条不会更新该值。

I had also tried the solution mentioned here but it doesn't work: 我也尝试过这里提到的解决方案,但是不起作用:
How do I update a progress bar in Cocoa during a long running loop? 如何在长时间运行的循环中更新可可中的进度条?

Can someone help to see where is the problem in my source code? 有人可以帮忙查看我的源代码中的问题吗?

Here is my source. 这是我的消息来源。

SimpleProgressBar.m 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 SimpleProgressBar.h

#import < Foundation/Foundation.h >

@interface SimpleProgressBar : NSObject{

    __weak NSProgressIndicator *progressBar;

}

@property (weak) IBOutlet NSProgressIndicator *progressBar;

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

Thank you very much for any helpful answer. 非常感谢您提供任何有用的答案。

Update: 更新:

Here is my porting from the solution and it doesn't work: 这是我从解决方案中移植过来的,它不起作用:

SimpleProgressBar.m 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);

}

Update: 更新:

A couple of observations: 一些观察:

  1. It strikes me that if you want to watch the NSProgressIndicator advance, you need to add a sleepForTimeInterval or else the for loop iterates so quickly that you won't see the progress indicator advance, but rather you'll just see it quickly end up in its final state. 令我NSProgressIndicator是,如果您想观看NSProgressIndicator前进,则需要添加sleepForTimeInterval ,否则for循环是如此之快,以至于您看不到进度指示器的前进,而只是看到它很快就结束了它的最终状态。 If you insert sleepForTimeInterval , you should see it progress: 如果插入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]; } 

    Or, if you wanted to do the for loop on a background thread, and dispatch the updates back to the main queue: 或者,如果您想在后台线程上执行for循环,然后将更新分派回主队列:

     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. You are using startAnimation and stopAnimation , but according to the documentation each of these "does nothing for a determinate progress indicator," so these calls seem inappropriate for this situation. 您正在使用startAnimationstopAnimation ,但是根据文档,每个这些“对于确定的进度指示器都不起作用”,因此这些调用似乎不适用于这种情况。


My original answer, below, was predicated on the comment in the Threads and Your User Interface in the Threading Programming Guide , which says: 以下是我的原始答案,根据《 线程编程指南》中“ 线程和用户界面”中的注释为准

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. 某些框架(例如Cocoa)通常需要此行为,但即使对于那些不需要的框架,在主线程上保留此行为也具有简化管理用户界面的逻辑的优势。

But the answer below is (incorrectly) an iOS answer, so is not applicable. 但是以下答案是(错误地)iOS答案,因此不适用。

Original answer: 原始答案:

Your for loop is running on the main thread, and thus UI updates won't appear until you yield back to the runloop. 您的for循环在主线程上运行,因此直到您让回到runloop之前,UI更新才会出现。 You're also going through that loop so quickly that even if you properly dispatched that to a background queue, you wouldn't experience the progress view changing as you iterate through your loop. 您还非常快地经历了该循环,即使您正确地将其分派到了后台队列,您也不会在循环浏览过程中体验进度视图的变化。

So, perform the loop on a secondary thread (eg via GCD or operation queue) and then dispatch UI updates back to the main thread, which is now free to do UI updates. 因此,在辅助线程上执行循环(例如,通过GCD或操作队列),然后将UI更新分派回主线程,该主线程现在可以自由进行UI更新。 So, using your theoretical example, you could do something like: 因此,使用理论示例,您可以执行以下操作:

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];
        });
    }
});

Note, having a loop that updates the progress view only makes sense if you're doing something slow enough for you to see the progress view change. 请注意,只有在您做得足够慢以使您看到进度视图更改时,才有一个循环来更新进度视图才有意义。 In your original example, you're just looping from 0 to 99, updating the progress view. 在原始示例中,您只是从0循环到99,从而更新了进度视图。 But that happens so quickly, that there's no point in a progress view in that case. 但这是如此之快,以至于在这种情况下,进度视图毫无意义。 That's why my above example not only employs a background queue for the loop, but also added a slight delay (via sleepForTimeInterval ). 这就是为什么我上面的示例不仅为循环使用了后台队列,而且还增加了一些延迟(通过sleepForTimeInterval )。

Let's consider a more realistic application of the progress view. 让我们考虑一下进度视图的更实际的应用。 For example, let's say I had an array, urls , of NSURL objects that represent items to be downloaded from the server. 例如,假设我有一个NSURL对象数组urls ,它们表示要从服务器下载的项目。 Then I might do something like: 然后我可能会做类似的事情:

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
        });
    }
});

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM