我正在异步下载图像并在UITableView中显示它们。下载图像时,UIProgressView应显示在相应的表格行中。下载完成后,进度视图应替换为实际图像。
在我的表视图中,我正在使用一个名为ProgressTableViewCell的自定义单元格,该单元格来自UITableViewCell。它有一个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];
}
这是我的视图控制器获取通知的方法:
-(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;
答案 0 :(得分: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)];
}
}
}
但是,你应该知道,如果你有比可见单元更多的下载,这个解决方案还不够 - 你还应该在数据源的某处存储每个下载的进度,这样如果表视图是否需要重新加载单元格(由于滚动),它知道如何设置进度指示器。
答案 1 :(得分:0)
我发现在很多情况下,你认为你拥有的单元格不是那个正在更新的单元格。当你重新加载时,你的代码会弹出可重用的单元格,这基本上是一个旧单元格。如果重新加载,该单元格将被另一个单元格替换。 (如果可重用的单元格返回nil,你还没有包括分配新的单元格。如果你的单元格滚动,它会被重新加载,所以你必须确保你将进度条放在那里的单元格上,而不是曾经在那里的人。您可能希望NSLog使用您开始传输的单元格的地址,然后每次更新进度时再次。您需要使用该索引路径上的当前单元格更新进度条。它可能不是您最初启动过程的那个。
答案 2 :(得分: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];
}
非常感谢大家的帮助。