Descargue imágenes de un server para mostrarlas en CollectionView

Estoy trabajando en una aplicación de producto donde el usuario podría vender / comprar. Esta aplicación se basa en la vista de colección. La vista de colección tiene una celda de colección donde muestra la miniatura de la image del producto.

El siguiente código obtiene imágenes de productos del server y espera download todas las imágenes y luego mostrarlas en las celdas. El siguiente código funciona, pero el usuario espera entre 10 y 20 segundos para ver todos los productos. ¿Hay una mejor manera de manejarlo?

- (void)viewDidLoad { [super viewDidLoad]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); dispatch_async(queue, ^(void) { [self loadFromURL]; dispatch_async(dispatch_get_main_queue(), ^{ }); }); } -(void)loadFromURL { NSURL *url = [NSURL URLWithString:@"http://myURL/productAll.php"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; operation.responseSerializer = [AFJSONResponseSerializer serializer]; [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { pElements= (NSMutableArray *)responseObject; [collectionView reloadData]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Product" message:[error localizedDescription]delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alertView show]; }]; [operation start]; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return pElements.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { ProductCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath]; cell.backgroundColor=[UIColor whiteColor]; NSString *arrayResult = [[pElements objectAtIndex:indexPath.row] objectForKey:@"image"]; NSData *data = [[NSData alloc] initWithBase64EncodedString:arrayResult options:NSDataBase64DecodingIgnoreUnknownCharacters]; cell.productImage.image = [[UIImage alloc] initWithData:data]; cell.productImage.layer.cornerRadius = 10; cell.productImage.clipsToBounds = YES; return cell; } 

Tiene una respuesta del server en el que los datos de image para todas las imágenes están codificados en la base 64 en la respuesta. Esto significa que la respuesta probablemente sea muy grande y no se mostrará al usuario hasta que se descargue todo.

En su lugar, podría considerar refactorizar el código de su server para no include los datos de la image en formatting base 64, sino simplemente include una URL (o algún identificador) que pueda usarse para recuperar la image más tarde. Su respuesta debería ser mucho más pequeña y debería poder procesarse mucho más rápidamente.

Luego, cuando se cellForItemAtIndexPath , en vez de extraer los datos de la image de la respuesta original, solicitamos perezosamente (y de forma asíncrona) la image de la celda. AFNetworking proporciona una buena categoría de UIImageView+AFNetworking en UIImageView+AFNetworking que recupera asincrónicamente imágenes de la fuente de la networking. (Y usar esta categoría te saca de la maleza de muchos problemas sutiles al hacer la recuperación de imágenes asíncronas).

Por cierto, si sus imágenes tienen tamaños variables, es posible que desee include las dimensiones de la image en la request original para que las celdas y sus vistas de image puedan tener un tamaño adecuado por adelantado, en lugar de cambiar el tamaño de las imágenes a medida que se recuperan las imágenes .

Un par de observaciones:

  1. No necesita enviar [self loadFromURL] a una queue de background, ya que ya es asíncrona. Y probablemente use GET de la request

  2. No puedes simplemente lanzar responseObject a NSMutableArray , porque probablemente no sea mutable. Realmente deberías usar NSArray o utilizar mutableCopy si realmente lo necesitas para que sea mutable.

  3. Estás haciendo alguna configuration de celda en cellForItemAtIndexPath . La mayor parte de eso (recorte, color de background, etc.) se puede hacer directamente en IB, por lo que lo haría allí en lugar de hacerlo mediante progtwigción. Lo único que deberías hacer mediante progtwigción es el networkingondeo de las esquinas (y, incluso eso, probablemente haría con una subclass IBDesignable , aunque eso está más allá del scope de esta pregunta).

Por lo tanto, suponiendo que (a) su matriz tiene una nueva propiedad llamada imageURL que es la URL de la image; y (b) la celda tiene una vista de image de tamaño fijo, puede hacer algo como:

 @interface ViewController () @property (nonatomic, strong) AFHTTPRequestOperationManager *manager; @property (nonatomic, strong) NSArray *pElements; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.manager = [AFHTTPRequestOperationManager manager]; [self loadFromURL]; } -(void)loadFromURL { NSString *urlString = @"http://myURL/productAll.php"; [self.manager GET:urlString parameters:nil success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) { self.pElements = responseObject; [self.collectionView reloadData]; } failure:^(AFHTTPRequestOperation * _Nullable operation, NSError * _Nonnull error) { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error Retrieving Product" message:[error localizedDescription]delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alertView show]; }]; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.pElements.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { ProductCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cellIdentifier" forIndexPath:indexPath]; NSString *imageURL = self.pElements[indexPath.row][@"imageURL"]; [cell.productImage setImageWithURL:[NSURL URLWithString:imageURL]]; cell.productImage.layer.cornerRadius = 10; return cell; } @end