2012-05-15 43 views
0

如果我有一个无害的循环,当我按下一个按钮一样,被发射了:可可触摸ProgressView循环中不显示更新

- (IBAction)start:(id)sender{ 
    calculatingProgressBar.progress = 0.0; 
    int j = 1000; 
    for (int i=0; i<j; i++) { 
     calculatingProgressBar.progress = (i/j); 
    } 
} 

没有更新,ProgressView。在其他语言中有一个视图更新命令,你必须在这些情况下执行。什么是可可等价物,或者你有什么其他的解决方法。

回答

4

问题在于,只有在运行(事件)循环转换时才会定期更新UI。我假设在主线程上运行的一个for()循环将阻止运行循环,直到完成。这不仅意味着进度条不会被更新(或者说,最终会跳到100%),但是您可能会冻结UI一段时间。 (我假设,因为你正在显示进度条,所以这可能需要一段时间。)

对于任何冗长的操作,你想要做的是在单独的队列/线程上完成工作,然后调用定期更新主线程上的UI。例如,你可以尝试像(警告:键入浏览器):

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); 
dispatch_async(queue, ^{ 
    int j = 1000; 
    for (int i=0; i<j; i++) { 
     // Do your presumably useful work, not just looping for fun :-) 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      // UI updates always come from the main queue! 
      calculatingProgressBar.progress = (i/j); 
     }); 
    } 
}); 

本例使用GCD(Grand Central Dispatch)。还有更高层次的结构,你可以看看,比如NSOperationQueue,但是基本的想法保持不变。

如果你不熟悉并发(多线程)编程,你将有一个适度的学习曲线来加快速度。我建议从Concurrency Programming Guide开始。

+0

该代码是否需要去?我天真地(?)把它放在我的(IBAction)部分,它似乎不以任何方式触发。代码编译但按钮被触发时没有任何反应... –

+0

实际调用了代码吗?尝试在每个dispatch_async调用中放置一个断点。 –

+0

@EdwardHasted我相信你有它的工作? –

-1

假设calculateProgressBar是一个IBOutlet,那么您可能只需要以符合KVO的方式设置新值,即使用setter:self.calculatingProgressBar.progress =(i/j);

+0

正如你可能已经聚集我在后VIRG在Xcode模式下。我渴望做得更多,但还有很多摸索。 我怀疑我已经得到了三个优秀的忠告,但还没有得到有效的努力,但他们还没有努力工作。 为自己添加前缀。在行中给出一条消息 属性'calculateProgressBar'找不到类型为'VC name'的对象... –

+0

XCode不是你的问题。可可是。 :) – Almo

+0

@EdwardHasted - 你有错误信息,因为你还没有宣布calculateProgressBar作为一个属性。我错了“自我”这个东西 - 你不需要那个。这里的其他答案指出了你的代码的一个主要问题。但是到目前为止所有人都错过的是你不能将i和j声明为整数,因为这样做会导致i/j总是等于0(因为你正在做整数运算,所以任何分数都为0)。如果您将它们声明为花车(或双打),您应该看到该栏立即跳到100%,因为由康拉德舒尔茨提到的运行循环问题 – rdelmar

1

当您在主线程上运行代码时,UI无法自行更新。这是有意的并且大大提高了性能。这意味着直到start返回时,UI才会发生更新。你多次更新progress,然后返回,所以它被绘制一次。

您将需要使用计时器或GCD队列来实现您正在谈论的内容。这取决于你的代码的其余部分看起来不少,但这里有一个方法,如果你希望它运行在60秒例如:

- (IBAction)start:(id)sender{ 
    calculatingProgressBar.progress = 0.0; 
    int j = 60; 

    dispatch_time_t now = dispatch_time(DISPATCH_TIME_NOW, 0); 
    for (int i=0; i<j; i++) { 
    dispatch_after(dispatch_time(now, i * NSEC_PER_SEC), 
        dispatch_get_current_queue(), 
        ^{ 
        calculatingProgressBar.progress = (i/j); 
        }); 
    } 
} 

这样做是建立了60对事件和调度他们相隔一秒钟。然后它返回(不运行它们)。

还有很多其他的方法,例如NSTimer或GCD定时器。您也可以使用dispatch_async()将处理移动到后台线程。(见康拉德·舒尔茨对这个问题的答案)

+0

我已经读过这个话题,并假设你不得不去旅行并欺骗UI来更新。当我尝试此代码时,我收到一条消息: 使用不兼容类型int的表达式初始化'const struct timespec'。 –

+0

消息删除 - 双张贴 –

+0

编辑。没有必要的技巧。您只需要按照系统预期的方式进行异步操作。 –

0

我觉得更优雅(和通用)解决方案是这样的: 在您的代理,有这这样的:

maxNumber = 1000; // define this somewhere, just for convenience 
... 
[self performSelectorInBackground:@selector(doLoop:) withObject:[NSNumber numberWithInt:maxNumber]]; 

有这样的方法:

- (void)doLoop:(NSNumber *)myMaxNumber { // this runs on the background 
    calculatingProgressBar.progress = 0.0; 
    NSNumber *j = myMaxNumber; 
    for (int i=0; i<j; i++) { 
    [self performSelectorOnMainThread:@selector(updateProgress:) withObject:[NSNumber numberWithInt:i] waitUntilDone:NO]; 
    } 
    // here you could call a method in the main thread, just to signify that your loop is done. 
    // [self performSelectorOnMainThread:@selector(loopDone:) withObject:nil waitUntilDone:NO]; 
} 

和更新都是在这里完成:

-(void)updateProgress:(NSNumber *)myNumber { 
    calculatingProgressBar.progress = ([myNumber intValue]/maxNumber); 
}