Индикатор UIProgressView не обновляется с правильным "прогрессом"

Я загружаю изображения асинхронно и отображаю их в UITableView. Во время загрузки изображения UIProgressView должен отображаться в соответствующей строке таблицы. После завершения загрузки вид прогресса должен быть заменен реальным изображением.

В моем табличном представлении я использую пользовательскую ячейку с именем ProgressTableViewCell, подклассами которой является UITableViewCell. Он имеет UIProgressView IBOutlet.

Я создал NSOperation из NSURLConnection и добавил их в 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, но безрезультатно. Мой обзор прогресса остается в нулевой точке. Иногда, когда я отлаживаю, я вижу некоторые изменения в индикаторе прогресса, которые заставляют меня думать, что это может быть проблема синхронизации. Но как это решить?

РЕДАКТИРОВАТЬ: Вот метод didReceiveData делегата NSURLConnection:

- (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];
}

И вот метод моего контроллера представления, который получает уведомление:

-(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];

}

Я также добавил проверку nil в мой метод cellForRowAtIndexpath, как рекомендовано:

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;

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)];
        }
    }
}

Вы должны знать, однако, что этого решения будет недостаточно, если у вас больше загрузок, чем видимых ячеек - вы также должны хранить ход каждой загрузки где-то в вашем источнике данных, так что если табличное представление ДОЛЖНО перезагрузить ячейка (из-за прокрутки), она знает, как установить индикатор прогресса.

Возвращаясь к этому почти через 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];
}

Большое спасибо всем за помощь.

Во многих подобных случаях я обнаружил, что ячейка, которую вы думаете, у вас не обновляется. Когда вы перезагружаете, ваш код выталкивает ячейку из повторно используемой, которая в основном является старой ячейкой. Если вы перезагрузите, эта ячейка заменяется другой. (Вы также не включили выделение новой ячейки, если повторно используемые возвращают ноль). Если ваша ячейка прокручивается, она перезагружается, поэтому вам нужно убедиться, что вы ставите индикатор выполнения на ячейку, которая там есть, а не на тот, который раньше был там. Возможно, вы захотите NSLog адрес ячейки, с которой вы начали передачу, а затем снова каждый раз, когда вы обновляете прогресс. Вам необходимо обновить индикатор выполнения, указав текущую ячейку по указанному пути индекса. Это может быть не тот, с которого вы изначально начали процесс.

Другие вопросы по тегам