Objective c 简单进度条未更新

Objective c 简单进度条未更新,objective-c,macos,cocoa,Objective C,Macos,Cocoa,我正在实现一个Cocoa应用程序,它只是一个简单的进度条,当我按下按钮时就会启动 情况是:当我按下按钮时,我可以看到动画开始和停止,但进度条不会更新值 我也尝试过这里提到的解决方案,但不起作用: 有人能帮我看看我的源代码中的问题在哪里吗 这是我的消息来源 SimpleProgressBar.m #import "SimpleProgressBar.h" @implementation SimpleProgressBar @synthesize progressBar; int flag=0

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

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

我也尝试过这里提到的解决方案,但不起作用:

有人能帮我看看我的源代码中的问题在哪里吗

这是我的消息来源

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

}
#导入“SimpleProgressBar.h”
@SimpleProgressBar的实现
@合成进度条;
int标志=0;
-(iAction)startProgressBar:(id)发送方{
如果(标志==0){
[self.progressBar startAnimation:sender];
flag=1;
}否则{
[self.progressBar停止动画:发送方];
flag=0;
}
[self.progressBar displayIfNeeded];
[self.progressBar setDoubleValue:10.0];
int i=0;
对于(i=0;i
@接口SimpleProgressBar:NSObject{
__弱NSProgressIndicator*progressBar;
}
@属性(弱)IBOutlet NSProgressIndicator*progressBar;
-(iAction)startProgressBar:(id)发送方;
@结束
非常感谢您的回答

更新:

以下是我从解决方案中移植的内容,但它不起作用:

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

}
#导入“SimpleProgressBar.h”
@SimpleProgressBar的实现
@合成进度条;
int标志=0;
-(iAction)startProgressBar:(id)发送方{
如果(标志==0){
[self.progressBar startAnimation:sender];
flag=1;
}否则{
[self.progressBar停止动画:发送方];
flag=0;
}
[self.progressBar displayIfNeeded];
[self.progressBar setDoubleValue:0.0];
无效(^progressBlock)(无效);
progressBlock=^{
[self.progressBar setDoubleValue:0.0];
int i=0;
对于(i=0;i更新:

以下是几点观察:

  • 我突然想到,如果你想观看
    NSProgressIndicator
    前进,你需要添加一个
    sleepForTimeInterval
    ,否则
    for
    循环的迭代速度太快,以至于你看不到进度指示器前进,而只会看到它很快就进入了最终状态。如果你插入
    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];
    }
    
  • 您正在使用
    startAnimation
    stopAnimation
    ,但根据这两种方法中的每一种,“对确定的进度指标不起任何作用”,因此这些调用似乎不适合这种情况


  • 下面是我的原始答案,它是根据《线程编程指南》中的注释得出的,其中说:

    如果应用程序具有图形用户界面,建议您从应用程序的主线程接收与用户相关的事件并启动界面更新。此方法有助于避免与处理用户事件和绘图窗口内容相关的同步问题。某些框架(如Cocoa)通常需要此方法行为,但即使对于那些不这样做的人来说,将此行为保留在主线程上的好处是简化了管理用户界面的逻辑

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

    原始答复:

    您的
    for
    循环正在主线程上运行,因此在您返回到runloop之前,UI更新不会出现。您也在快速地完成该循环,即使您正确地将其调度到后台队列,您也不会在迭代循环时体验到进度视图的更改

    因此,在辅助线程上执行循环(例如,通过GCD或操作队列),然后将UI更新发送回主线程,主线程现在可以自由进行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];
            });
        }
    });
    
    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
            });
        }
    });
    
    更新:

    以下是几点观察:

  • 我突然想到,如果你想观看
    NSProgressIndicator
    前进,你需要添加一个
    sleepForTimeInterval
    ,否则
    for
    循环的迭代速度太快,以至于你看不到进度指示器前进,而只会看到它很快就进入了最终状态。如果你插入
    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];
    }
    
  • 您正在使用
    startAnimation
    stopAnimation
    ,但根据这两种方法中的每一种,“对确定的进度指标不起任何作用”,因此这些调用似乎不适合这种情况


  • 下面是我的原始答案,它是根据《线程编程指南》中的注释得出的,其中说:

    如果应用程序具有图形用户界面,建议您从应用程序的主线程接收与用户相关的事件并启动界面更新。此方法有助于避免与处理用户事件和绘图窗口内容相关的同步问题。某些框架(如Cocoa)通常需要此方法行为,但即使对于那些不这样做的人来说,将此行为保留在主线程上的好处是简化了管理用户界面的逻辑

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

    原始答复:

    您的
    for
    循环正在主线程上运行,因此在您返回到runloop之前,UI更新不会出现。您也在快速地完成该循环,即使您正确地将其调度到后台队列,您也不会在迭代循环时体验到进度视图的更改

    因此,在辅助线程上执行循环(例如,通过GCD或操作队列),然后将UI更新发送回主线程,主线程现在可以自由进行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];
            });
        }
    });
    
    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
            });
        }
    });
    

    嗨,Rob,我移植了正确的代码来运行辅助线程上的更新吗?我是Xcode的新手,你能帮我看看我的代码有什么问题吗