Problema de performance mientras se difumina la image en UITableViewCell

UIImageView un UIImageView en mi UITableViewCell personalizado. La image incluida debe ser borrosa. Estoy al tanto de UIVisualEffectsView pero antes de nada, esto no está disponible antes de iOS8 y, segundo, el desenfoque es un poco pesado para mi caso de uso.

Es por eso que se me ocurrió esta solución:

Muestra cellForRowAtIndexPath:

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellIdentifier = @"showCell"; DEShowCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if (cell == nil) { [tableView registerNib:[UINib nibWithNibName:@"DEShowCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:cellIdentifier]; cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; } [cell setBackgroundImageWithBlur:[UIImage imageNamed:@"sampleBanner"]]; return cell; } 

Mi celda personalizada:

 -(void)setBackgroundImageWithBlur:(UIImage *)image { [self.backgroundImageView setImage:[self blurWithCoreImage:image]]; } - (UIImage *)blurWithCoreImage:(UIImage *)sourceImage { CIImage *inputImage = [CIImage imageWithCGImage:sourceImage.CGImage]; // Apply Affine-Clamp filter to stretch the image so that it does not // look shrunken when gaussian blur is applied CGAffineTransform transform = CGAffineTransformIdentity; CIFilter *clampFilter = [CIFilter filterWithName:@"CIAffineClamp"]; [clampFilter setValue:inputImage forKey:@"inputImage"]; [clampFilter setValue:[NSValue valueWithBytes:&transform objCType:@encode(CGAffineTransform)] forKey:@"inputTransform"]; // Apply gaussian blur filter with radius of 30 CIFilter *gaussianBlurFilter = [CIFilter filterWithName: @"CIGaussianBlur"]; [gaussianBlurFilter setValue:clampFilter.outputImage forKey: @"inputImage"]; [gaussianBlurFilter setValue:@10 forKey:@"inputRadius"]; CIContext *context = [CIContext contextWithOptions:nil]; CGImageRef cgImage = [context createCGImage:gaussianBlurFilter.outputImage fromRect:[inputImage extent]]; // Set up output context. UIGraphicsBeginImageContext(self.frame.size); CGContextRef outputContext = UIGraphicsGetCurrentContext(); // Invert image coordinates CGContextScaleCTM(outputContext, 1.0, -1.0); CGContextTranslateCTM(outputContext, 0, -self.frame.size.height); // Draw base image. CGContextDrawImage(outputContext, self.frame, cgImage); // Apply white tint CGContextSaveGState(outputContext); CGContextSetFillColorWithColor(outputContext, [UIColor colorWithWhite:1 alpha:0.2].CGColor); CGContextFillRect(outputContext, self.frame); CGContextRestoreGState(outputContext); // Output image is ready. UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return outputImage; } 

Lamentablemente, esto me UITableView enormes problemas de performance cuando trato de desplazarme por UITableView .

¿Entonces me pregunté cómo solucionarlo? Podría usar alguna biblioteca para hacer el desenfoque como GPUImage que será más rápido, supongo, pero no sé si esto puede hacer una gran diferencia.

La UITableView contendrá aproximadamente 20-60 filas , creo.

¿Alguna otra idea, como el almacenamiento en caching o alguna otra cosa?

Usaré FXBlurView con la configuration dinámica = NO. Tiene un performance decente.

No hay ninguna razón para seguir computando la image de background borrosa. Como todo está usando el mismo, simplemente desenfoca una vez y asígnala cuando configuras la celda:

 if (cell == nil) { [tableView registerNib:[UINib nibWithNibName:@"DEShowCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:cellIdentifier]; cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; // Inside the initial configuration; not outside. [cell.backgroundImageView setImage:self.blurnetworkingBackgroundImage]; } 

Configurar self.blurnetworkingBackgroundImage una vez:

 - (void)viewDidLoad { self.blurnetworkingBackgroundImage = [self blurWithCoreImage:[UIImage imageNamed:...]]; } 

Por supuesto, dado este código, probablemente solo borre la image en PhotoShop y guárdela como el recurso. No hay ninguna razón para desenfocar mediante progtwigción un recurso estático (a less que esté usando la misma image en otro lugar y sea lo suficientemente grande como para que el espacio en disco sea importante).

Si desea usar CoreImage, no debe crear constantemente el context que va a usar con él. Debe crear el context una vez y volver a usarlo, ya que al no hacerlo puede causar este tipo de problema de performance.

Puede averiguar si ese es el problema en el código anterior o si se trata de algo más al perfilar su código utilizando el instrumento del perfilador de time. Para hacer eso, simplemente select "Perfil" en el menu "Producto" en Xcode y cuando se ejecute, elija "Analizador de time". A continuación, podrá ver un rastro de stack de las llamadas que ocuparon más time.

Una de las muestras de código de WWDC 2013 , iOS_UIImageEffects, recorre el uso del marco de alto performance de vImage para desenfocar imágenes. Cuando lo probé, fue considerablemente más rápido que CoreImage.

Pero en respuesta a su pregunta sobre el almacenamiento en caching, sí, absolutamente, emplear el almacenamiento en caching. Use NSCache como caching principal y memory persistente como caching secundaria.


Por cierto, parece que se está borrando la misma image para cada celda. Si es así, debe desenfocarlo una vez y luego reutilizar esa image borrosa para todas las celdas.

Del mismo modo, está registrando el NIB varias veces. Regístralo una vez al frente.