2012-11-29 6 views
1

이미지를 비동기 적으로 다운로드하고 UITableView에 표시하고 있습니다. 이미지가 다운로드되는 동안 해당 테이블 행에 UIProgressView가 표시되어야합니다. 다운로드가 완료되면 진행보기가 실제 이미지로 바뀝니다.UIProgressView 표시기가 올바른 "진행률"로 업데이트되지 않습니다.

내 테이블보기에서 UITableViewCell에서 서브 클래 싱 된 ProgressTableViewCell이라는 사용자 지정 셀을 사용하고 있습니다. 그것은 UIProgressView IBOutlet을 가지고 있습니다.

NSURLConnection에서 NSOperation을 만들고 NSOperationQueue에 추가했습니다. 대리인의

didReceiveData

메서드를 호출 할 때, 알림이

reloadRowsAtIndexPaths

테이블보기의 방법으로 해당 테이블의 행을 업데이트 내 테이블 뷰 컨트롤러에 게시됩니다. 내 cellForRowAtIndexPath는 다시로드 된 행에 대해 다음 작업을 수행합니다 내가 주 스레드에서 진행보기를 업데이트하고

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

처럼

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 방법을보고 난 YES로 이에 waitUntilDone 설정 시도 아무 소용이. 내 진행 뷰는 영점에 머물러 있습니다. 때로는 디버깅 할 때 진행 표시기에 약간의 변화가있어서 타이밍 문제 일 수 있다고 생각할 수도 있습니다. 그러나 그것을 해결하는 방법?

편집 : 여기에있는 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]; 
} 

그리고 여기에 통지 도착 내보기 컨트롤러의 방법입니다 :

-(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 방법에 전무 체크를 추가 한을 권장 사항 :

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; 
+0

알림을 처리하는 데 사용하는 대리자 메서드 및 메서드를 게시 할 수 있습니까? 내 생각 엔 알림 경로와 다운로드 정보를 알림 처리기에서 사용할 수 있다면 매번 테이블보기를 다시로드하는 대신 진행률보기를 업데이트해야합니다. –

+0

나는 Joe라는 방법을 게시했다. 위의 내용은 EDIT에서 볼 수 있습니다. 문제는 다운로드중인 각 이미지에 대해 진행률보기가 있으므로 테이블 뷰의 데이터 소스 대리자 메서드 내에서 업데이트를 수행하는 이유입니다. – ganime

답변

0

많은 경우에 l 당신이 가지고 있다고 생각하는 세포가 업데이트하는 세포가 아니라는 점이 이것입니다. 다시로드하면 코드에서 재사용 가능한 셀을 팝하는 중입니다.이 셀은 기본적으로 오래된 셀입니다. 다시로드하면 해당 셀이 다른 셀로 대체됩니다. (재사용 가능한 셀이 nil을 반환하면 새 셀을 할당하는 것도 포함되지 않습니다.) 셀이 스크롤되지 않으면 다시로드되므로 진행 막대를 셀에 놓고 있는지 확인해야합니다. 한 번 거기에 있었어. 전송을 시작한 셀의 주소를 NSLog로 설정 한 다음 진행 상황을 업데이트 할 때마다 다시 NSLog로 설정할 수 있습니다. 진행률 막대를 해당 색인 경로의 현재 셀로 업데이트해야합니다. 처음에는 그 과정을 시작한 사람이 아닐 수도 있습니다.

+0

참 오웬. NSLog로 셀 주소를 입력 할 때마다 매번 다른 주소를 얻습니다. 그런데 왜 세포 재사용으로 이런 일이 일어나고 있습니까? 또한 하나의 이미지 다운로드 만있을 때 어떻게 다른 프로그램보기가 업데이트되고 있습니까? 어떻게 "나는 거기에있는 셀에 진행 막대를 놓고 있는지 확인해야합니다." ? – ganime

+1

세포를 재사용 할 때, 그게 바로 당신이하고있는 일입니다. 행 0에 있었던 셀은 스크롤 할 때 행 12에서 재사용됩니다. 따라서 다운로드를 시작하는 데 사용 된 셀 객체가 같은 위치에 있지 않습니다. 사용자 정의 셀 객체를 사용하여 어떤 셀에 내 다운로드 객체가 있는지 파악하고 그 셀을 검색하여 찾을 수 있습니다. 다운로드 셀이 화면 밖으로 스크롤 될 수도 있습니다. 새로운 셀을로드/재사용 할 때 파일 다운로드에 대한 참조를 업데이트하는 것이 중요합니다. –

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

당신은 당신이 볼 수 세포보다 더 많은 다운로드가있는 경우이 솔루션은 충분하지 않습니다,하지만 알고 있어야를 - 테이블 뷰가 셀을 다시로드해야하는 경우 (스크롤로 인해) 진행률 표시기를 설정하는 방법을 알 수 있도록 데이터 소스의 어딘가에 각 다운로드 진행 상황을 저장해야합니다.

+0

마침내 해결책을 찾았습니다 (내 대답보기). 하지만 오늘 당신의 제안을 시도했는데 효과가있는 것 같습니다. 왜 내가 2 개월 전에 그것을 작동시키지 못했는지를 기억할 수 없다. 나는 당신이 언급 한 이유들로 더 많은 일을해야하기 때문에 그것을하는 다른 방법을 찾아갔습니다. 어쨌든 고마워. – ganime

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

많은 도움을 주신 모든 분들께 감사드립니다.

관련 문제