2012-09-07 31 views
15

Actualmente estoy intentando cargar una lista UITableView de Flickr Photo (cs193p iOS Stanford, asignación 5). Para evitar el evento de bloqueo de la interfaz de usuario, he diferido la descarga en miniatura de cada celda en una cola diferente (pero actualizo la interfaz de usuario en la cola principal). Este código no carga asíncronamente las imágenes, aunque sí agrega una miniatura cuando hago clic en la fila UITableViewCell. (ver capturas de pantalla a continuación). ¿Alguna idea de lo que estoy haciendo mal?Carga de imagen UITableViewCell asincrónica con GCD

PD: Ya he buscado en algunas otras preguntas de stackoverflow & ejemplo de LazyTableImages de Apple, pero sigo convencido de que esta es la forma más limpia de lograr el resultado deseado.

Gracias!

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Photo List Cell"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    // Configure the cell 
    NSDictionary *photo = [self.photoList objectAtIndex:indexPath.row]; 


    if (photo != nil) { 
     if ([[photo objectForKey:@"title"] length] > 0) { 
      cell.textLabel.text = [photo objectForKey:@"title"]; 
     } else if ([[[photo objectForKey:@"description"] objectForKey:@"_content"] length] > 0) { 
      cell.textLabel.text = [[photo objectForKey:@"description"] objectForKey:@"_content"]; 
     } else { 
      cell.textLabel.text = @"Unknown"; 
     } 
    } 
    cell.imageView.image = [[UIImage alloc] initWithCIImage:nil]; 

    // Fetch using GCD 
    dispatch_queue_t downloadThumbnailQueue = dispatch_queue_create("Get Photo Thumbnail", NULL); 
    dispatch_async(downloadThumbnailQueue, ^{ 
     UIImage *image = [self getTopPlacePhotoThumbnail:photo]; 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      if ([self.tableView.visibleCells containsObject:cell]) { 
       [cell.imageView setImage:image]; 
      } 
     }); 
    }); 
    dispatch_release(downloadThumbnailQueue); 

    return cell; 
} 

Antes clic en una fila Before clicking a row

Después de seleccionar la fila After selecting the row

ACTUALIZACIÓN: Para los interesados, este es el código final utilicé:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Photo List Cell"; 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    // Configure the cell 
    NSDictionary *photo = [self.photoList objectAtIndex:indexPath.row]; 

    if (photo != nil) { 
    if ([[photo objectForKey:@"title"] length] > 0) { 
     cell.textLabel.text = [photo objectForKey:@"title"]; 
    } else if ([[[photo objectForKey:@"description"] objectForKey:@"_content"] length] > 0) { 
     cell.textLabel.text = [[photo objectForKey:@"description"] objectForKey:@"_content"]; 
    } else { 
     cell.textLabel.text = @"Unknown"; 
    } 
    } 
    cell.imageView.image = [[UIImage alloc] initWithCIImage:nil]; 

    // Fetch using GCD 
    dispatch_queue_t downloadThumbnailQueue = dispatch_queue_create("Get Photo Thumbnail", NULL); 
    dispatch_async(downloadThumbnailQueue, ^{ 
    UIImage *image = [self getTopPlacePhotoThumbnail:photo]; 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     UITableViewCell *cellToUpdate = [self.tableView cellForRowAtIndexPath:indexPath]; // create a copy of the cell to avoid keeping a strong pointer to "cell" since that one may have been reused by the time the block is ready to update it. 
     if (cellToUpdate != nil) { 
      [cellToUpdate.imageView setImage:image]; 
      [cellToUpdate setNeedsLayout]; 
     } 
    }); 
    }); 
    dispatch_release(downloadThumbnailQueue); 

    return cell; 
} 
+0

Hey, esto es totalmente fuera de tema, pero se pregunta si el interior del bloque que usted envíe al hilo principal, en lugar de comprobar la igualdad objeto en la 'celda', compruebe si existe una celda en' indexPath' –

+0

Acabo de probar esto: if ([self.tableView cellForRowAtIndexPath: indexPath]! = Nil) {... etc} y funciona igual de bien también. ¿Eso es lo que tenías en mente? – sybohy

+0

Correcto, y no usa 'cell.imageView' directamente en su bloque, sino que toma la celda en' indexPath' y usa su vista de imagen. Creo que podría tener problemas con la reutilización de células si usa 'cell' directamente de la forma en que se encuentra dentro de su bloque de hilos principal. –

Respuesta

15

Debe llamar al setNeedsLayout en la celda después de configurar la miniatura.

Desde el Doc de Apple:.

"llamar a este método en el subproceso principal de la aplicación cuando se quiere ajustar el diseño de subvistas de una vista Este método hace una nota de la solicitud y devuelve inmediatamente porque este método. no forzar una actualización inmediata, sino que espera el siguiente ciclo de actualización, puede usarlo para invalidar el diseño de varias vistas antes de actualizar cualquiera de esas vistas. Este comportamiento le permite consolidar todas sus actualizaciones de diseño en un ciclo de actualización, que generalmente es mejor para el rendimiento ".

+0

wow.¡eso fue rápido! Gracias Carl, nunca lo habría averiguado por mi cuenta. ¡¡Gracias de nuevo!! – sybohy

+0

De nada, ¡me alegro de que haya funcionado! –

+0

En realidad, pregunta rápida: ¿técnicamente no sería mejor llamar a [cell setNeedsLayout]? Estaba leyendo el documento de Apple para layoutSubviews y esto es lo que encontré: "No debe llamar a este método [layoutSubviews] directamente. Si desea forzar una actualización de diseño, llame al método setNeedsLayout en lugar de hacerlo antes de la próxima actualización de dibujo " ¿Qué piensas? (ambos funcionan funcionalmente por cierto) – sybohy

0

Otra solución (y la que utilicé para esa asignación) es llamar a setNeedsDisplay en el imageView de la celda una vez que has asignado el UIImage a ese imageView.

Así que, básicamente, creé una clase llamada FlickrDownloader que completa la funcionalidad de FlickrFetcher (así como mi código de almacenamiento en caché). FlickrDownloader tiene este método:

(void)getImageForPhotoInfo:(NSDictionary *)photoInfo ofFormat:(FlickrPhotoFormat)format withCallback:(image_setter_callback_t)callback; 

Esto es, básicamente, una llamada a la misma función de FlickrFetcher, pero añade una devolución de llamada eso se llama a la finalización de la descarga de la foto. En el caso de la actualización del imageView de un UITableViewCell la devolución de llamada es el siguiente:

image_setter_callback_t setImage = ^(UIImage *image){ 
    cell.imageView.image = image; 
    [cell.imageView setNeedsDisplay]; 
}; 
+0

¿Pasó eso por la celda configurando el marco a '(0,0,0,0)'? Cuando probé algo similar, no cambió de tamaño hasta que forcé a 'layoutSubviews' a ser llamado. ¿Estabas usando una imagen de marcador de posición tal vez? –

+0

si llama a [cell.imageView setNeedsDisplay], ¿eso no requiere que su clase implemente el método drawRect? Revisando la conferencia 4 de otoño de 2011, diapositiva # 8, setNeedsDisplay configura todo para llamar a drawRect. Realmente no dice si hace más o menos que eso. ¿lo hace? – sybohy

+0

@CarlVeazey Tiene razón en que utilicé una imagen de marcador de posición como el ejemplo de LazyTableImages. Como este era mi enfoque desde el principio, tal vez así fue como logré establecer el marco. –

Cuestiones relacionadas