3

ここで説明されているように、スレッドを使用して進行状況バーを更新したいと思います。私はこの結果を達成しようとしています:

  1. プログレスバーが表示される
  2. プログレスバーはループを使用して更新されます
  3. プログレスバーが消える

これは私のコードです:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        
        progressBar.hidden = NO;

        for (NSInteger i = 1; i <= progressBar.maxValue; i += 20){

            
                [NSThread sleepForTimeInterval:1.0];
                dispatch_async(dispatch_get_main_queue(), ^{
                    [progressBar setDoubleValue:(double)i];
                    [progressBar displayIfNeeded];
                });
            }
        
        progressBar.hidden = YES;
        
    });

進行状況バーは、ViewController.h int で次のように定義されています。

NSProgressIndicator *progressBar

問題は、ループの最後でバーが削除されないことです。このように機能するかどうかはわかりませんprogressBar.hidden = YES;

誰かが私を助けることができますか?コード スニペットは、特に説明が続く場合に非常に役立ちます。

4

2 に答える 2

5

あなたがしていることは、2 つの理由で良くありません。

まず、スレッドを所有しているか、スレッドの責任を正確に把握していない限り、スレッドをスリープ状態にすることは適切ではありません。キューのスレッドは GCD が所有しているため、下位レベルではなく、キューのレベルで作業を行う必要があります。(メイン キューからのブロックは常にメイン スレッドで実行されます。限られた状況ではありますが、グローバル キューからのブロックはバックグラウンド スレッドで実行されない場合があります。*)

第二に、あなたが尋ねた問題の原因: バックグラウンド スレッド上にあるため、あなたの設定はhidden非メイン スレッドでの UI 操作です。UI の状態で同期の問題が発生する可能性があるため、これは許可されていません。メイン スレッド以外から Cocoa でビューの外観を変更するのは安全ではありません。

setDoubleValue:呼び出しのためにメイン キューにディスパッチすることで、その問題は半分正解ですが、設定hiddenはメイン スレッドでも行う必要があります。

for ループは、画面を更新するための適切なメカニズムではありません。代わりにメソッドを繰り返し呼び出すように手順を作り直すことをお勧めします。NSTimerは、あなたがしていることを行うために構築されています。使用例を見つけるのに問題はないはずです。

GCD を使用したい場合は、単一のdispatch_after()呼び出しを使用するように切り替え、メイン キューでの遅延の後にブロックを繰り返し実行することをお勧めします。このようなもの:

- (void)kickItOff
{
    self.progressBar.hidden = NO;
    [self updateProgress:0];
}

- (void)updateProgress:(double)progressValue
{
    if( self.progressBar.maxValue <= progressValue ){
        self.progressBar.hidden = YES;
        return;
    }

    dispatch_time_t oneSecond = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC));
    dispatch_after(oneSecond, dispatch_get_main_queue(), ^{

        [self.progressBar setDoubleValue:progressValue];
        [self updateProgress:progressValue + 20];
    });
}

を呼び出して更新サイクルを開始しますkickItOff。次にupdateProgress:、ループを配置します。これにより、必要な間隔でコードが実行されるようにしながら、メイン スレッドと実行ループがスムーズに動作し続けることができます。


*この点の詳細: UI を実際に画面に描画するには、メインの実行ループが循環している必要があります。メイン スレッドがスリープ状態の場合、それは起こり得ません。描画と入力の受け入れの両方で UI 全体がロックされます (メイン ディスパッチ キューも処理されません)。

于 2016-12-24T23:10:24.627 に答える