2010-12-23 16 views
20

Queríamos utilizar una UITabBar en nuestra aplicación para iPhone, pero con una excepción: tenemos un botón de "sincronización" que quería girar mientras se realiza la operación de sincronización.Animación principal Animación de máscara CALayer

alt text

Desafortunadamente esto significaba tener que crear una barra de pestañas a medida, pero eso es no viene al caso: la animación he implementado usando Core Animation se ve impresionante. El problema es que al animar, afecta negativamente el rendimiento de todo lo demás mediante la animación en la pantalla: desplazamiento UITableView, panorámica MKMapView y pin gotas, etc. Mi dispositivo de prueba es un iPhone 4.

El problema parece ser cómo Implementé la barra de pestañas. Quería lograr algo muy similar a UITabBar, donde solo proporcionas un PNG para el ícono y usa el canal alfa para crear los estados normal y resaltado enmascarando una imagen de fondo. He logrado esto con mask propiedad 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 pantalla de arriba se puede ver también he añadido una capa de sombra, pero para simplificar Quité que a partir del código quito la capa de sombra de. . el icono de sincronización cuando está animando, por lo que no debe ser relevante)

animar, me basta con girar 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 
} 

así que todo esto funciona genial, excepto para el rendimiento. Puedes ver que ya probé algunos consejos que aparecieron en Google sobre rasterizar capas/hacerlas opacas, no ha ayudado.

Creo que he identificado la capa de máscara como la culpable. Cuando llevo a cabo la capa de máscara y Basta girar la fgLayer en lugar de su máscara, el rendimiento es maravillosa, aunque ciertamente no es el afecto Voy para:

alt text

rendimiento también es tan malo como antes si giro fgLayer en lugar de la máscara mientras que se aplica la máscara.

Entonces, si al volver a unir la máscara cada fotograma de la animación es la ralentización, ¿hay otras técnicas que pueda utilizar para lograr un efecto similar que tenga un mejor rendimiento? ¿Usa una ruta en lugar de una imagen para la capa de máscara? ¿O tendré que pasar a OpenGL o algo para obtener un buen rendimiento?

ACTUALIZACIÓN: reforzando aún más la idea de que la máscara es la desaceleración, mi compañero de trabajo sugirió tratando de girar una CALayer con sólo la imagen como el contenido - de modo similar al ejemplo anterior w máscara/oa - y el rendimiento también fue bueno de esa manera. Así que realmente solo puedo hacer un color sólido como ese (sin degradado), pero puede ser una buena solución provisional. Todavía me encanta para lograr la rotación de una máscara con un buen rendimiento aunque, por lo sugerencia bienvenida :)

Respuesta

15

Brent,

¿Por qué necesita usar una máscara de capa? ¿No puedes simplemente convertir tu capa de máscara en una subcapa? Solo necesitarás asegurarte de que tu imagen tenga el alfa correcto y usarla como contenido de esa capa.

Una cosa más.Todavía no entiendo por qué, pero también noté problemas de rendimiento cuando aplico shouldRasterize en cada capa en lugar de solo la capa superior. Puede ver si elimina la referencia a setShouldRasterize: YES en su capa de máscara ayuda en absoluto.

+0

La segunda parte de su respuesta fue la solución: debería haber estado configurando 'shouldRasterize' en la capa superior en lugar de las subcapas. Esto parece haber hecho que el rendimiento sea tan bueno como sin la máscara. La razón por la que "necesito" una máscara es para poder "llenar" los iconos PNG simples con un degradado agradable para agregar interés visual. El UITabBar predeterminado hace esto, por lo que quería hacer lo mismo (pero con amarillo). –

+0

Cool, Brent. Me alegro de que haya sido así de simple. Si obtiene alguna idea del * por qué * del asunto, me gustaría saberlo. ;-) –

+0

Me encantaría saber por qué también. Además, me encantaría saber * cómo * rasterizar la capa superior realmente mejora el rendimiento de una animación de máscara. Sigue haciendo exactamente lo que yo quiero: la imagen del degradado de respaldo permanece estacionaria, mientras que la máscara está girando. ¿Está realmente precomputando el mapa de bits para cada cuadro antes de que comience la animación? Parece magia. En este caso, me gusta la magia. :) –

7

Un enfoque sería crear un CAShapeLayer para usar como máscara: tendría un poco de trabajo de recorte para usted haciendo una versión de su icono de "sincronización" con las rutas de Bézier, pero las capas de forma incurren en un menor costo de rendimiento por transformación que los de mapa de bits. Desafortunadamente, no se puede estar seguro de que la rotación sea la causa del problema de rendimiento; bien podría ser el enmascaramiento que provocó la mayor parte del retraso, en cuyo caso habrá realizado todas las operaciones de vectorización para obtener pocos beneficios.

Creo que la mejor solución es usar las capacidades de animación de UIImage: crea una tira de película de cada fotograma de la animación de rotación de iconos y simplemente muestra esa animación UIImage en tu barra de pestañas. No es la solución más elegante, pero una serie de animaciones en todo el sistema -el icono "eliminar" de Mail y Notes, y las diversas formas de indicador de actividad, por ejemplo- se implementan de la misma manera.

+2

Gracias por la respuesta: usar una tira de película de la animación definitivamente era algo que estaba considerando, pero sonaba como un montón de espacio de trabajo + archivo. Afortunadamente 'shouldRasterize' en el * superlayer * parece haber logrado el rendimiento que esperaba. –

+0

De la respuesta, parece que "set shouldRasterize to NO" resolvió su problema. – Shuo

Cuestiones relacionadas