2012-08-31 11 views
5

Creo que esta es una pregunta bastante compleja. Tengo un TableView que muestra una cantidad de contenido descargable. Cuando hace clic en un botón dentro de la celda, comienza la descarga.Forma correcta de mostrar contenido descargable en UITableView (con ProgressBar etc.)

pero estoy teniendo varios problemas: 1.How puedo hacer para asegurar que el progressBar se mostrará todo el tiempo (incluso si se volverá a cargar los rollos usuario se desplaza y la célula) 2.How puedo hacer Seguro que el usuario puede descargar 2 archivos a la vez. Me temo que causa problemas porque utilizo algunas variables de instancia. En una forma en que debe trabajar un poco como descarga desde iCloud en la aplicación Music

Aquí está mi código

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

Sería bueno si alguien tiene una idea o un buen ejemplo de código

+0

Si necesita la barra de progreso que se muestra todo el tiempo, es probable que no desee colocarla en una celda de la tabla. ¿O quiere decir que siempre muestra cuándo está visible la celda en la ruta de índice del elemento de descarga? –

+0

Quiero mostrar la barra de progreso en la celda correspondiente – arnoapp

Respuesta

20

Es importante tener en cuenta que sus barras de progreso no se mostrarán todo el tiempo (es decir, el usuario puede desplazar la tabla y una vez fuera de la pantalla puede reutilizarse en otra posición de índice para contenido diferente). Entonces, lo que tendrá que hacer es tener un sitio donde pueda almacenar los datos sobre las descargas activas, incluida la posición del índice en la tabla, el tamaño total del archivo y la cantidad de bytes descargados hasta el momento. Luego, cada vez que se extraiga su celda, deberá verificar si el elemento de esa celda se está descargando actualmente y, de ser así, mostrar la barra con el porcentaje de progreso adecuado.

La manera más fácil de hacer esto sería agregar una propiedad a su controlador de vista para almacenar esta información. Puede ser un NSMutablerray que contendrá una colección de objetos NSMutableDictionary, cada diccionario contendrá la información necesaria sobre una descarga activa.

@property (nonatomic, strong) NSMutableArray *activeConnections; 

Lo primero será inicializar la matriz en viewDidLoad::

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    //... 

    self.activeConnections = [[NSMutableArray alloc] init]; 
} 

Cada vez que se pulsa un botón, agregará un objeto NSMutableDictionary a su matriz con la información que necesita.

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

También crearemos dos métodos de utilidad para que podamos encontrar fácilmente recuperar la información de la conexión de nuestra matriz, utilizando el mismo objeto de conexión, o la posición de índice de la fila en la tabla.

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

Cuando se establece la conexión, actualizar su diccionario con la longitud esperada  

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response 
{ 
    // ... 

    NSDictionary *dict = [self getConnectionInfo:connection]; 
    [dict setObject:[NSNumber numberWithInt:response.expectedContentLength] forKey:@"totalFileSize"]; 
} 

Al recibir los datos, podrás actualizar el número de bytes recibidos y decirle a su tableView para volver a dibujar la célula que contiene la barra de progreso.

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

Cuando la conexión se realiza la descarga, debe eliminar la conexión de su panel activeConnections, y volver a cargar la celda de tabla.

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

Por último, en cellForRowAtIndexPath: que necesita para llamar la barra de progreso de la celda en base a la información en la matriz 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]; 
    } 
} 
+0

Creo que esta respuesta es incompleta. NSDictionary no tiene el método ** setObject: **. El flujo de código es incorrecto. Aprecio tu respuesta, pero edita esta respuesta para que los usuarios puedan entenderla. Thnkyou –

+0

Esta solución es genial, pero usaría un 'NSMutableDictionary' en lugar de un' NSMutableArray', por lo que es más rápido recuperar el objeto de él. @MSwapnil si necesita usar 'setObject: forKey:' luego use 'NSMutableDictionary' en lugar de' NSDictionary'. –

3

Su El problema es que estás mezclando tus datos y tu UI. Confía en cada celda de la vista de tabla para almacenar su estado actual de descarga, lo que provocará problemas. En su lugar, debe crear un objeto personalizado que pueda almacenar los datos que necesita para una celda (es decir, un bool que indica si se está descargando, el progreso de la descarga actual, los datos descargados, etc.). A continuación, debe tener una matriz mutable de estos objetos, de modo que objectAtIndex:0 sean los datos de la primera celda de la tabla y así sucesivamente.

Cuando comienza una descarga, actualice el objeto relevante en la matriz. Idealmente, cuando crea las celdas de la vista de tabla, debe usar el NSKeyValueObserving protocol para que se les notifique automáticamente cuando sus datos subyacentes hayan cambiado y puedan actualizar sus datos de manera apropiada. Cuando se reutiliza una celda, estará bien porque el objeto que almacena sus datos seguirá allí, y si necesita volver a estarlo porque está a punto de volver a la pantalla, podrá usar esos datos para mostrarse correctamente.

Deseo saber si necesita más explicaciones sobre cualquier parte de esto.

+0

Gracias por la respuesta, creo que estoy entendiendo la idea. Pero me pregunto cómo interiorizar una matriz así en el lugar correcto. ¿Esto también resolverá el problema de la paralelización? – arnoapp

+0

Debería comenzar creando una subclase de 'NSObject' que tenga todos los datos que necesita para la celda. ¿Entiendes lo que eso significa? Y sí, resolverá el problema de la paralelización. – Ander

+0

¿Qué sucede si un usuario vuelve a la pantalla anterior y regresa nuevamente? ¿Permanecerá el estado de descarga o aún actualizará el progreso? –

Cuestiones relacionadas