1

画像を非同期でダウンロードし、UITableView に表示しています。イメージのダウンロード中、対応するテーブル行に UIProgressView が表示されます。ダウンロードが完了すると、進行状況ビューが実際のイメージに置き換えられます。

私のテーブル ビューでは、UITableViewCell からサブクラス化された ProgressTableViewCell というカスタム セルを使用しています。UIProgressView IBOutlet があります。

NSURLConnection から NSOperation を作成し、それらを NSOperationQueue に追加しました。代表として

didReceiveData

メソッドが呼び出されると、テーブルビューコントローラーに通知が投稿され、対応するテーブル行が更新されます

reloadRowsAtIndexPaths

テーブルビューの方法。私の cellForRowAtIndexPath は、リロードされた行に対して次のことを行います。

   ProgressTableViewCell *cell = (ProgressTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"ProgressCell"];

    float received = [[downloadInfo objectForKey:@"receivedBytes"] floatValue];
    float total = [[downloadInfo objectForKey:@"totalFileSize"] floatValue];

    NSNumber* percentage= [NSNumber numberWithFloat:received/total];
    NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
    NSLog(@"percentage %f", percentage.floatValue);
    [userInfo setObject:cell forKey:@"cell"]; 
    [userInfo setObject:percentage forKey:@"percentage"];  

    [self performSelectorOnMainThread:@selector(updateProgressView:) withObject:userInfo waitUntilDone:NO];
    NSLog(@"received: %@", [downloadInfo objectForKey:@"receivedBytes"]);

    NSLog(@"Progress: %f", cell.progressView.progress);
    return cell;

updateProgressView メソッドは次のようになります

- (void)updateProgressView :(NSMutableDictionary *)userInfo
{
    ProgressTableViewCell* cell = [userInfo valueForKey:@"cell"];

    NSNumber* progress = [userInfo valueForKey:@"percentage"];

   [cell.progressView setProgress:progress.floatValue ];
    NSLog(@"Progress after update: %f", cell.progressView.progress);
}

メイン スレッドの進行状況ビューを更新しています。waitUntilDone を YES に設定しようとしましたが、役に立ちませんでした。私の進行状況ビューはゼロ点のままです。時折、デバッグ中に進行状況インジケーターに変化が見られ、タイミングの問題ではないかと思われることがあります。しかし、それを解決する方法は?

編集: NSURLConnection デリゲートの didReceiveData メソッドは次のとおりです。

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_responseData appendData:data];
    NSNumber* bytes = [NSNumber numberWithUnsignedInt:[data length]];

    NSLog(@"received bytes:%d", [bytes intValue] );
    NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
    [userInfo setObject:_responseId forKey:@"responseId"];  
    [userInfo setObject:bytes forKey:@"receivedBytes"];

    [self fireNotification: [NSNotification
                                   notificationWithName:@"DidReceiveData"
                                   object:self userInfo:userInfo]];
}



- (void)fireNotification :(NSNotification *)aNotification
{
    [[NSNotificationCenter defaultCenter] postNotification:aNotification];
}

そして、通知を受け取るView Controllerのメソッドは次のとおりです。

-(void) dataReceived:(NSNotification *)notification {

    NSNumber* responseId = [[notification userInfo] objectForKey:@"responseId"];
    NSNumber*  bytes = [[notification userInfo] objectForKey:@"receivedBytes"];

    NSMutableDictionary* downloadInfo = [self getConnectionInfoForId:responseId];

    NSLog(@"received bytes:%ld for response %@", [bytes longValue], responseId );
    NSNumber* totalBytes = [NSNumber numberWithInt:([bytes longValue] + [[downloadInfo objectForKey:@"receivedBytes"] longValue]) ];
    [downloadInfo setObject:totalBytes forKey:@"receivedBytes"];

    float received = [[downloadInfo objectForKey:@"receivedBytes"] floatValue];
    float total = [[downloadInfo objectForKey:@"totalFileSize"] floatValue];

    [downloadInfo setObject:[NSNumber numberWithFloat:received/total] forKey:@"progress"];

    [self reloadRowForResponse:responseId];

}

また、推奨されるように cellForRowAtIndexpath メソッドに nil チェックを追加しました。

ProgressTableViewCell *cell = (ProgressTableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"ProgressCell"];
    if (cell == nil)
    {
        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"ProgressCell" owner:self options:nil];
        cell = [nib objectAtIndex:0];
    }
    float received = [[downloadInfo objectForKey:@"receivedBytes"] floatValue];
    float total = [[downloadInfo objectForKey:@"totalFileSize"] floatValue];

    NSNumber* percentage= [NSNumber numberWithFloat:received/total];
    NSMutableDictionary* userInfo = [[NSMutableDictionary alloc] init];
    NSLog(@"cell:%@", cell);
    NSLog(@"percentage %f", percentage.floatValue);
    [userInfo setObject:cell forKey:@"cell"];  
    [userInfo setObject:percentage forKey:@"percentage"]; 

    [self performSelectorOnMainThread:@selector(updateProgressView:) withObject:userInfo waitUntilDone:NO];

    return cell;
4

3 に答える 3

3

デリゲート メソッドが呼び出されるたびにテーブル セルをリロードするという間違ったアプローチを取っていると思います。代わりに、データ ソースを経由するのではなく、表示されているセルを取得して進行状況インジケーターを直接更新することができます。

responseId更新したい行のインデックスパスに変換する何らかの方法があると仮定しています- それindexPathForResponseID:があなたのコントローラで呼び出されたとしましょう。セルをリロードするのではなく、セルが表示されている場合はセルを取得して、進行状況インジケーターを更新することができます。

- (void)dataReceived:(NSNotification *)notification {

    ...

    float received = [[downloadInfo objectForKey:@"receivedBytes"] floatValue];
    float total = [[downloadInfo objectForKey:@"totalFileSize"] floatValue];

    NSIndexPath *cellPath = [self indexPathForResponseID:responseId];
    ProgressTableViewCell *cell = (ProgressTableviewCell *)[self.tableView cellForRowAtIndexPath:cellPath];
    if (cell) {
        // make sure you're on the main thread to update UI
        dispatch_async(dispatch_get_main_queue(), ^{ 
            [cell.progressView setProgress: (received / total)];
        }
    }
}

ただし、表示されているセルよりも多くのダウンロードがある場合、このソリューションでは不十分であることに注意してください。また、各ダウンロードの進行状況をデータ ソースのどこかに保存して、テーブル ビューをリロードする必要がある場合に備えてください。セル (スクロールによる)、進行状況インジケーターの設定方法を知っています。

于 2012-11-30T19:22:24.247 に答える
0

ほぼ2か月後にこれに戻って、私は解決策を見つけているようです。これが良い習慣かどうかはわかりませんが、進行状況が更新されるたびに新しいUIProgressViewを作成すると、私の問題は解決するようです。方法は次のとおりです。

-(void)updateProgressView :(NSMutableDictionary *)userInfo
{
    ProgressTableViewCell* cell = [userInfo objectForKey:@"cell"];
    cell.backgroundColor =[UIColor darkGrayColor];
    NSNumber* progress = [userInfo objectForKey:@"percentage"];
    NSLog(@"Progress before update: %f", cell.progressView.progress);

    UIProgressView *pView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
    pView.frame = CGRectMake(150, 200, 150, 9);
    pView.progress = progress.floatValue;

    [cell.contentView addSubview:pView];
}

皆様のご協力に感謝いたします。

于 2013-01-19T22:25:54.837 に答える
0

このような多くの場合、自分が持っていると思っているセルが更新されているセルではないことがわかりました。リロードすると、コードは再利用可能なセルからセルをポップします。これは基本的に古いセルです。リロードすると、そのセルは別のセルに置き換えられます。(再利用可能なセルが nil を返した場合の新しいセルの割り当ても含まれていません)かつてそこにあったもの。転送を開始したセルのアドレスを NSLog に記録し、進行状況を更新するたびに再度記録することをお勧めします。そのインデックス パスの現在のセルで進行状況バーを更新する必要があります。最初にプロセスを開始したものではない可能性があります。

于 2012-11-29T21:44:38.837 に答える