在UITableView中显示可下载内容的正确方法(使用ProgressBar等)

我认为这是一个相当复杂的问题。 我有一个TableView,显示一些可下载的内容。 当您单击单元格内的button时,将开始下载。

但我有几个问题:1.如何确保progressBar将始终显示(即使用户滚动滚动,单元格将被重新加载)2.如何确保用户可以下载2个文件一次。 恐怕会导致问题,因为我使用了一些实例variables。 在某种程度上,它应该有点像在音乐应用程序从iCloud下载

这是我的代码

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:nil]; } //cell.tag = indexPath.row*10; Uebungsblaetter *uebungCell = [uebungsblattArray objectAtIndex:indexPath.row]; cell.tag = indexPath.row*10; cell.textLabel.text = [self getFileNameOutOf:uebungCell.url]; cell.textLabel.textColor = [UIColor grayColor]; cell.selectionStyle = UITableViewCellSelectionStyleNone; UIButton *dl = [UIButton buttonWithType:UIButtonTypeCustom]; dl.tag = indexPath.row*10; [dl setBackgroundImage:[UIImage imageNamed:@"downloadButton.png"] forState:UIControlStateNormal]; [dl setBackgroundImage:[UIImage imageNamed:@"downloadButtonH.png"] forState:UIControlStateHighlighted]; [dl setFrame:CGRectMake(230.0, (cell.frame.size.height-28)/2, 28, 28)]; [dl addTarget:self action:@selector(downloadFileWhenPressedButton:) forControlEvents:UIControlEventTouchUpInside]; [cell.contentView addSubview:dl]; UIProgressView *dlProgress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; dlProgress.frame = CGRectMake(cell.frame.size.width-150, 17, 50, 9); dlProgress.tag =indexPath.row*10+1; dlProgress.progress = 0.0; [cell.contentView addSubview:dlProgress]; [dlProgress setHidden:YES]; return cell; } //download methods - (void)downloadFileWhenPressedButton:(UIButton*)sender{ sender.hidden = YES; dlIndex = sender.tag/10; Uebungsblaetter *selectedUB = [uebungsblattArray objectAtIndex:dlIndex]; NSURL *theUrl = [NSURL URLWithString:selectedUB.url]; NSURLRequest *req=[NSURLRequest requestWithURL:theUrl cachePolicy:NSURLCacheStorageNotAllowed timeoutInterval:120]; dlCell = (UITableViewCell *)[[sender superview]superview]; currDlProgress = (UIProgressView* )[dlCell.contentView viewWithTag:dlIndex*10+1]; currDlProgress.hidden = NO; NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); dlFilePath = [NSString stringWithFormat:@"%@/%@_%@", [paths objectAtIndex:0],self.courseLable.text,[self getFileNameOutOf:selectedUB.url]]; NSURLConnection *con=[[NSURLConnection alloc] initWithRequest:req delegate:self startImmediately:YES]; if (con) { myWebData = [NSMutableData data]; } } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{ UIApplication* app = [UIApplication sharedApplication]; app.networkActivityIndicatorVisible = YES; currDlProgress.progress = 0; _totalFileSize = response.expectedContentLength; NSLog(@"%@",@"connection established"); [myWebData setLength: 0]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { _receivedDataBytes += [data length]; currDlProgress.progress = _receivedDataBytes / (float)_totalFileSize; NSLog(@"%@",@"connection receiving data"); [myWebData appendData:data]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"%@",@"connection failed"); // [AlertViewHandler showAlertWithErrorMessage:@"Sorry, there is no network connection. Please check your network and try again."]; // [self parserDidEndDocument:nil]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { UIApplication* app = [UIApplication sharedApplication]; app.networkActivityIndicatorVisible = NO; [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.35]; [currDlProgress setAlpha:0]; [UIView commitAnimations]; [myWebData writeToFile:dlFilePath atomically:YES]; Uebungsblaetter *loadedUB = [uebungsblattArray objectAtIndex:dlIndex]; loadedUB.downloaded = [NSNumber numberWithBool:YES]; [courseTable reloadData]; } 

如果有人有一个线索或一个很好的代码示例会很好

认识到你的进度条不会一直显示是非常重要的(即用户可以滚动表格,一旦离开屏幕,同一个单元格可以在另一个索引位置重复使用不同的内容)。 因此,您需要做的是在某处存储有关任何活动下载的数据,包括表中的索引位置,文件总大小以及到目前为止所下载的字节数。 然后,每当你的单元格被绘制,你需要检查该单元格的项目是否正在下载,如果是,则显示适当的百分比进度条。

最简单的方法是添加一个属性到你的视图控制器来存储这个信息。 它可以是一个NSMutablerray ,它将包含一个NSMutableDictionary对象的集合,每个字典将包含关于一个活动下载的必要信息。

 @property (nonatomic, strong) NSMutableArray *activeConnections; 

首先你要在viewDidLoad:初始化数组

 - (void)viewDidLoad { [super viewDidLoad]; //... self.activeConnections = [[NSMutableArray alloc] init]; } 

每当按下一个button时,您都会使用所需的信息将一个NSMutableDictionary对象添加到您的数组中。

 - (void)downloadFileWhenPressedButton:(UIButton*)sender { // ... // then create dictionary NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; [dict setObject:con forKey:@"connection"]; // save connection so we can reference later [dict setObject:[NSNumber numberWithInt:[sender.tag]/10] forKey:@"row"]; // this is the row index from your table [dict setObject:[NSNumber numberWithInt:999] forKey:@"totalFileSize"]; // dummy size, we will update when we know more [dict setObject:[NSNumber numberWithInt:0] forKey:@"receivedBytes"]; [self.activeConnections addObject:dict]; } 

另外,我们将创build两个实用方法,以便我们可以使用连接对象本身或表中的行索引位置轻松地从数组中检索连接信息。

 - (NSDictionary*)getConnectionInfo:(NSURLConnection*)connection { for (NSDictionary *dict in self.activeConnections) { if ([dict objectForKey:@"connection"] == connection) { return dict; } } return nil; } - (NSDictionary*)getConnectionInfoForRow:(int)row { for (NSDictionary *dict in self.activeConnections) { if ([[dict objectForKey:@"row"] intValue] == row) { return dict; } } return nil; } 

当连接build立时,用你想要的长度更新你的字典

 - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { // ... NSDictionary *dict = [self getConnectionInfo:connection]; [dict setObject:[NSNumber numberWithInt:response.expectedContentLength] forKey:@"totalFileSize"]; } 

当你接收到数据时,你将更新接收到的字节数,并告诉你的tableView重绘包含进度条的单元格。

 - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { // ... NSDictionary *dict = [self getConnectionInfo:connection]; NSNumber bytes = [data length] + [[dict objectForKey:@"receivedBytes"] intValue]; [dict setObject:[NSNumber numberWithInt:response.expectedContentLength] forKey:@"receivedBytes"]; int row = [[dict objectForKey:@"row"] intValue]; NSIndexPath *indexPath = [NSIndexPathindexPathForRow:row inSection:0]; [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; } 

连接完成后,您应该从activeConnectionsarrays中删除连接,然后重新加载表格单元格。

 - (void)connectionDidFinishLoading:(NSURLConnection *)connection { // ... NSDictionary *dict = [self getConnectionInfo:connection]; [self.activeConnections removeObject:dict]; int row = [[dict objectForKey:@"row"] intValue]; NSIndexPath *indexPath = [NSIndexPathindexPathForRow:row inSection:0]; [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; } 

最后,在cellForRowAtIndexPath:您需要根据activeConnections数组中的信息绘制单元格的进度条。

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { // ... // remove any previous buttons or progress bars from this cell for (UIView *view in [cell.contentView subViews]) { if ([view isKindOfClass:[UIProgressView class]] || [view isKindOfClass:[UIButton class]]) { [view removeFromSuperView]; } } // look for active connecton for this cell NSDictionary *dict = [self getConnectionInfoForRow:indexPath.row]; if (dict) { // there is an active download for this cell, show a progress bar UIProgressView *dlProgress = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; dlProgress.frame = CGRectMake(cell.frame.size.width-150, 17, 50, 9); dlProgress.tag = indexPath.row*10+1; dlProgress.progress = [[dict objectForKey:@"receivedBytes"] intValue] / [[dict objectForKey:@"totalFileSize"] intValue]; [cell.contentView addSubview:dlProgress]; } else { // no active download, show the download button UIButton *dl = [UIButton buttonWithType:UIButtonTypeCustom]; dl.tag = indexPath.row*10; [dl setBackgroundImage:[UIImage imageNamed:@"downloadButton.png"] forState:UIControlStateNormal]; [dl setBackgroundImage:[UIImage imageNamed:@"downloadButtonH.png"] forState:UIControlStateHighlighted]; [dl setFrame:CGRectMake(230.0, (cell.frame.size.height-28)/2, 28, 28)]; [dl addTarget:self action:@selector(downloadFileWhenPressedButton:) forControlEvents:UIControlEventTouchUpInside]; [cell.contentView addSubview:dl]; } } 

你的问题是你正在混合你的数据和你的用户界面。 你依靠每个表视图单元格来存储它的当前下载状态,这将导致问题。 相反,你应该创build一个自定义对象,可以存储单元格所需的数据(例如,一个bool指示是否正在下载,当前的下载进度,下载的数据等)。 然后,您应该有这些对象的可变数组,以便objectAtIndex:0是表中第一个单元格的数据,依此类推。

下载开始时,更新数组中的相关对象。 理想情况下,当您创build表格视图单元格时,您应该使用NSKeyValueObserving协议,以便在底层数据发生更改并自动更新数据时自动通知它们。 当一个单元格被重新使用的时候,这样做会很好,因为存储数据的对象将仍然存在,如果需要重新生成,因为它将要返回到屏幕上,那么它将能够使用该数据来正确显示。

让我知道,如果你需要任何进一步的解释在这方面的任何部分。