Core Animation CALayer máscara de animation de performance

Queríamos utilizar un UITabBar en nuestra aplicación para iPhone, pero con una exception: tenemos un button de "synchronization" que quería rotar mientras se está ejecutando la synchronization.

texto alternativo

Lamentablemente, esto significó tener que crear una barra de tabs personalizada, pero eso no es ni aquí ni allí: la animation que implementé usando Core Animation se ve increíble. El problema es que mientras se anima, afecta negativamente el performance de todo lo demás utilizando animation en la pantalla: Desplazamiento UITableView, panoramización MKMapView y caídas de pin, etc. Mi dispositivo de testing es un iPhone 4.

El problema parece ser la forma en que implementé la barra de tabs: quería lograr algo muy similar a UITabBar, donde solo proporciona un PNG para el ícono y usa el canal alfa para crear estados normales y resaltados enmascarando un background image. Logré esto con la propiedad de mask de CALayer:

 // Inside a UIView subclass' init method... // Create the mask layer by settings its contents as our PNG icon. CALayer *maskLayer = [CALayer layer]; maskLayer.frame = CGRectMake(0, 0, 31, 31); maskLayer.contentsGravity = kCAGravityCenter; maskLayer.contentsScale = [[UIScreen mainScreen] scale]; maskLayer.rasterizationScale = [[UIScreen mainScreen] scale]; maskLayer.contents = (id)symbolImage.CGImage; maskLayer.shouldRasterize = YES; maskLayer.opaque = YES; fgLayer = [[CALayer layer] retain]; fgLayer.frame = self.layer.frame; fgLayer.backgroundColor = [UIColor colorWithImageNamed:@"tabbar-normal-bg.png"].CGColor; fgLayer.mask = maskLayer; // Apply the mask fgLayer.shouldRasterize = YES; fgLayer.opaque = YES; [self.layer addSublayer:fgLayer]; 

(Nota: en la captura de pantalla arriba puedes ver que también agregué una capa de sombra, pero para simplificar, la eliminé del código. Quito la capa de sombra del ícono de synchronization cuando está animando, por lo que no debería ser relevante .)

Para animar, simplemente giro la capa de máscara:

 - (void)startAnimating { CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath: @"transform"]; CATransform3D transform = CATransform3DMakeRotation(RADIANS(179.9), 0.0, 0.0, 1.0); animation.toValue = [NSValue valueWithCATransform3D:transform]; animation.duration = 5; animation.repeatCount = 10000; animation.removedOnCompletion = YES; [fgLayer.mask addAnimation:animation forKey:@"rotate"]; // Add animation to the mask } 

Entonces, todo funciona muy bien, excepto por el performance. Puede ver que ya he probado los consejos que aparecieron en Google sobre el rasterizado de capas / que los hace opacos, no ha ayudado.

Creo que he identificado la capa de la máscara como el culpable. Cuando saco la capa de máscara y simplemente rotar el fgLayer lugar de su máscara, el performance es maravilloso, aunque ciertamente no es el efecto que voy a tomar:

texto alternativo

El performance también es tan malo como antes si giro el fgLayer lugar de la máscara mientras se aplica la máscara.

Entonces, si tener que recomponer la máscara cada fotogtwig de la animation es la ralentización, ¿hay alguna otra técnica que pueda usar para lograr un efecto similar que tenga un mejor performance? ¿Usando una ruta en lugar de una image para la capa de máscara? ¿O voy a tener que abrir OpenGL o algo para get un buen performance?

ACTUALIZACIÓN: reforzando aún más la idea de que la máscara es la ralentización, mi compañero de trabajo sugirió tratar de rotar un CALayer con solo la image como el contenido, tan similar a mi ejemplo anterior con una máscara, y el performance también fue bueno de esa manera. Así que realmente solo puedo hacer un color sólido como ese (sin gradiente), pero puede ser una buena solución provisional. Todavía me encantaría lograr rotar una máscara con un buen performance, por lo que sugerencias bienvenidas 🙂

Brent,

¿Por qué necesitas usar una máscara de capa? ¿No puedes convertir tu capa de máscara en una subcapa? Solo necesitaría asegurarse de que su image tuviera el alfa correcto y usaría su CGImageRef como el contenido de esa capa.

Otra cosa. Aún no he descubierto por qué, pero también he notado problemas de performance cuando aplico shouldRasterize en cada capa en lugar de solo en la capa superior. Puede ver si la eliminación de la reference a setShouldRasterize: YES en su capa de máscara ayuda en absoluto.

Un enfoque sería crear un CAShapeLayer para usarlo como tu máscara; tendrías que trabajar un poco para hacer una versión de tu ícono de "synchronization" con routes de Bézier, pero las capas de forms tienen un costo de performance mucho menor por -transformación que los maps de bits. Lamentablemente, no puede estar seguro de que la rotation es de donde proviene el problema de performance: muy bien podría ser el enmascaramiento causante de la mayoría del retraso, en cuyo caso habrá hecho todo ese vectorización con poco beneficio.

Creo que la mejor solución es utilizar las capacidades de animation de UIImage : crea una tira de película de cada fotogtwig de la animation de rotation de icons y simplemente visualiza ese UIImage animado en tu barra de tabs. No es la solución más elegante, pero una serie de animaciones en el sistema (la papelera de correo y notas) puede "eliminar" el ícono y las diversas forms de indicador de actividad, por ejemplo, se implementan de la misma manera.