2012-05-15 41 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); 
}