UICollectionView despliega el performance utilizando AFNetworking para cargar imágenes

He leído algunas de las publicaciones de UICollectionView sobre el desplazamiento deficiente, pero ninguna parece aplicarse directamente o todavía no se han respondido.

Estoy utilizando AFNetworking para cargar de forma asincrónica las imágenes (95px al cuadrado) en cada celda y luego cuando las imágenes vuelven a visualizarse, la image se restaura del caching (según lo verificado por el código de respuesta dado como 0 en lugar de 200).

Esto es lo que he intentado:

  • Se ha comentado weakCell.photoView.image = image; por lo que las imágenes no se dibujan en la pantalla y el desplazamiento fue más suave (aún se tartamudeó un poco durante la obtención de HTTP)
  • Se eliminó todo el código cellForRowAtIndexPath método cellForRowAtIndexPath y el desplazamiento fue mucho más suave (incluso con las sombras de celdas personalizadas, etc. aún dibujadas en la pantalla)
  • Cuando dibujo solo la vista de celda (con las sombras) en la pantalla, el desplazamiento es muy suave para 100 celdas. Tan pronto como empiezo a dibujar las imágenes en la pantalla, el desplazamiento es muy pobre en mi dispositivo y es incluso perceptible en el simulador. Instagram tiene un desplazamiento muy suave para cientos de celdas en su vista de perfil, por lo que trato de acercarme a su performance.

¿Hay alguna forma en que pueda mejorar cualquiera de mis códigos a continuación para mejorar el performance de desplazamiento?

Aquí está mi código de celular:

 #import "PhotoGalleryCell.h" @implementation PhotoGalleryCell - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { // Setup the background color, shadow, and border self.backgroundColor = [UIColor colorWithWhite:0.25f alpha:1.0f]; self.layer.borderColor = [UIColor blackColor].CGColor; self.layer.borderWidth = 0.5f; self.layer.shadowColor = [UIColor blackColor].CGColor; self.layer.shadowRadius = 3.0f; self.layer.shadowOffset = CGSizeMake(0.0f, 2.0f); self.layer.shadowOpacity = 0.5f; // Make sure we rasterize for retina self.layer.rasterizationScale = [UIScreen mainScreen].scale; self.layer.shouldRasterize = YES; // Add to the content view self.photoView = [[UIImageView alloc] initWithFrame:self.bounds]; [self.contentView addSubview:self.photoView]; } return self; } - (void)prepareForReuse { [super prepareForReuse]; self.photoView.image = nil; self.largeImageURL = nil; } 

Y aquí está mi código de UICollectionView:

 #pragma mark - Collection View Delegates - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView { return 1; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return [zePhotos count]; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { PhotoGalleryCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kPGPhotoCellIdentifier forIndexPath:indexPath]; // Get a reference to the image dictionary NSDictionary *photoDict = [[zePhotos objectAtIndex:indexPath.row] objectForKey:@"image"]; // Asynchronously set the thumbnail view __weak PhotoGalleryCell *weakCell = cell; NSString *thumbnailURL = [[photoDict objectForKey:@"thumbnail"] objectForKey:@"url"]; NSURLRequest *photoRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:thumbnailURL]]; [cell.photoView setImageWithURLRequest:photoRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { weakCell.photoView.image = image; } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"Error retrieving thumbnail... %@", [error localizedDescription]); }]; // Cache the large image URL in case they tap on this cell later cell.largeImageURL = [[photoDict objectForKey:@"large"] objectForKey:@"url"]; return cell; } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { [self performSegueWithIdentifier:@"showPhotoDetail" sender:self]; } 

Podría intentar agregar un shadowPath a su cell init, debería mejorar el performance, ese es el código que usé en uno de mis proyectos para agregar un shadowPath networkingondeado (ver los methods de UIBezierPath para más opciones)

 self.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.frame.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10, 10)].CGPath; 

Además, si recuerdo correctamente AFNetworking no cambia el tamaño de la image devuelta por el server, por lo que podría tener un impacto en la calidad de su image (a pesar del método de escala que agregó a UIImageView), recomiendo enviar la image devuelta para cambiar el tamaño si quieres así:

 CGSize targetSize = cell.photoView.bounds.size; [cell.photoView setImageWithURLRequest:photoRequest placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ CGFloat imageHeight = image.size.height; CGFloat imageWidth = image.size.width; CGSize newSize = weakCell.imageView.bounds.size; CGFloat scaleFactor = targetSize.width / imageWidth; newSize.height = imageHeight * scaleFactor; UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); [image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)]; UIImage *small = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); dispatch_async(dispatch_get_main_queue(),^{ weakCell.photoView.image = small; }); }); } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) { NSLog(@"Error retrieving thumbnail... %@", [error localizedDescription]); }]; 

La inspección de código se ve bien, aunque apuesto a que es la composition de la sombra lo que agrega mucho al retraso. La forma en que descubre exactamente lo que está causando la demora es usar la herramienta Time Profiler en Instruments. Aquí están los documentos de Apple .

El problema es que cuando se desplaza rápidamente, está iniciando cientos de requestes de networking al mismo time. Si tiene la image en caching, visualícela de inmediato. Si no lo hace, solo inicie la descarga cuando la vista de tabla se ralentice.

Puedes usar algo como esto:

 //Properties or Instance Variables NSDate *scrollDateBuffer; CGPoint scrollOffsetBuffer; - (void)scrollViewDidScroll:(UIScrollView *)scrollView { NSTimeInterval secondsSinceLastScroll = [[NSDate date] timeIntervalSinceDate:scrollDateBuffer]; CGFloat distanceSinceLastScroll = fabsf(scrollView.contentOffset.y - scrollOffsetBuffer.y); BOOL slow = (secondsSinceLastScroll > 0 && secondsSinceLastScroll < 0.02); BOOL small = (distanceSinceLastScroll > 0 && distanceSinceLastScroll < 1); if (slow && small) { [self loadImagesForOnscreenRows]; } scrollDateBuffer = [NSDate date]; scrollOffsetBuffer = scrollView.contentOffset; } 

loadImagesForOnscreenRows llamar a loadImagesForOnscreenRows en otros methods, como cuando entran nuevos datos, viewWillAppear y scrollViewDidScrollToTop .

Aquí hay una implementación de ejemplo de loadImagesForOnscreenRows :

 - (void)loadImagesForOnscreenRows { @try { for (UITableViewCell *cell in self.tableView.visibleCells) { // load your images NSURLRequest *photoRequest = …; if (photoRequest) { [cell.photoView setImageWithURLRequest:…]; } } } @catch (NSException *exception) { NSLog(@"Exception when loading table cells: %@", exception); } } 

Lo tengo en un bloque try / catch porque en mi experiencia [UITableView -visibleCells] no es confiable, de vez en cuando devuelve células o células desasignadas sin una supervisión. Si se asegura de que este método solo se llama cuando la tabla no se desplaza rápidamente, no debe afectar demasiado el performance de desplazamiento.

Además, tenga en count que la categoría UIImageView de AFNetworking no expone el object de caching. Tendrá que modificarlo ligeramente para comprobar si ya tiene una image almacenada en caching; esta respuesta debería apuntarte en la dirección correcta.