iphone - Core Animation CALayer máscara de rendimiento de animación
objective-c ios (2)
Brent,
¿Por qué necesitas usar una máscara de capa? ¿No puedes simplemente convertir tu capa de máscara en una subcapa? Solo deberías asegurarte de que tu imagen tenga el alfa adecuado y usarás CGImageRef como contenido de esa capa.
Otra cosa. Todavía no he descubierto por qué, pero también he notado problemas de rendimiento cuando aplico shouldRasterize en cada capa en lugar de solo la capa superior. Podría ver si eliminar la referencia a setShouldRasterize: SÍ en su capa de máscara ayuda en todo.
Queríamos usar una barra de UIT en nuestra aplicación para iPhone, pero con una excepción: tenemos un botón de "sincronización" que quería rotar mientras ocurre la operación de sincronización.
Desafortunadamente, esto significaba tener que crear una barra de pestañas personalizada, pero eso no es aquí ni allí: la animación que implementé usando Core Animation se ve increíble. El problema es que, al animar, afecta negativamente el rendimiento de todo lo demás mediante la animación en la pantalla: el desplazamiento de UITableView, el desplazamiento de MKMapView y la caída de pines, etc. Mi dispositivo de prueba es un iPhone 4.
El problema parece ser cómo he implementado la barra de pestañas: quería lograr algo muy similar a UITabBar, donde solo proporcionas un PNG para el icono y usa el canal alfa para crear los estados normales y resaltados al enmascarar un fondo. imagen. 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 anterior puede ver que también he agregado una capa de sombra, pero para simplificar, quité eso del código. Quito la capa de sombra del icono de sincronización 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
}
Así que todo esto funciona muy bien, excepto por el rendimiento. Puede ver que ya he intentado sugerencias que aparecieron en Google sobre rasterizar capas / hacerlas opacas - no ha ayudado.
Creo que he identificado la capa de máscara como el culpable. Cuando saco la capa de máscara y simplemente giro el fgLayer
lugar de su máscara, el rendimiento es maravilloso, aunque ciertamente no es el efecto que estoy buscando:
El rendimiento 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 fotograma de la animación es más lento, ¿hay otras técnicas que pueda usar para lograr un efecto similar que tenga un mejor rendimiento? ¿Usar una ruta en lugar de una imagen para la capa de máscara? ¿O tendré que bajar 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ó tratar de rotar un CALayer con solo la imagen como contenido, similar a mi ejemplo anterior con una máscara, y el rendimiento 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. Aún así me encantaría lograr rotar una máscara con un buen rendimiento, así que las sugerencias son bienvenidas :)
Un enfoque sería crear un CAShapeLayer
para usarlo como su máscara: usted tendría un poco de trabajo por hacer una versión de su ícono de "sincronización" con las rutas Bézier, pero las capas de formas incurren en un costo de rendimiento mucho menor. -Transformación de los bitmap. Desafortunadamente, no puede estar seguro de que la rotación es de donde proviene el problema de rendimiento; podría ser el enmascaramiento que causa la mayor parte del retraso, en cuyo caso habrá hecho toda esa vectorización para obtener un pequeño beneficio.
Creo que la mejor solución es usar las capacidades de animación de UIImage
: cree una tira de película de cada fotograma de la animación de rotación de iconos y simplemente muestre ese UIImage
animado en su barra de pestañas. No es la solución más elegante, pero varias animaciones en todo el sistema, por ejemplo, el ícono de "eliminación" de la papelera de Correo y Notas y las diversas formas de indicador de actividad, se implementan de la misma manera.