2012-02-21 5 views
13

Tengo la sensación de que estoy pasando por alto algo elemental, pero ¿qué mejor manera de encontrarlo que estar equivocado en Internet?Propiedades de CAGradientLayer no animadas dentro del bloque de animación UIView

Tengo una interfaz de usuario bastante básica. La vista para mi UIViewController es una subclase cuyo +layerClass es CAGradientLayer. Dependiendo de las acciones del usuario, necesito mover algunos elementos de UI y cambiar los valores del degradado del fondo. El código es como la siguiente:

[UIView animateWithDuration:0.3 animations:^{ 
    self.subview1.frame = CGRectMake(...); 
    self.subview2.frame = CGRectMake(...); 
    self.subview2.alpha = 0; 

    NSArray* newColors = [NSArray arrayWithObjects: 
         (id)firstColor.CGColor, 
         (id)secondColor.CGColor, 
         nil]; 
    [(CAGradientLayer *)self.layer setColors:newColors]; 
}]; 

La cuestión es que los cambios que realice en este bloque a los subvistas animan bien (cosas se mueve y se desvanece), pero el cambio de colores del degradado no lo hace. Simplemente cambia.

Ahora, the documentation does say ese código de Core Animation dentro de un bloque de animación no heredará las propiedades del bloque (duración, suavizado, etc.). ¿Pero es el caso que eso no define una transacción de animación en absoluto? (La implicación de los documentos parece ser que obtendrá una animación por defecto, donde consigo ninguna.)

¿Me tengo utilizar explícita CAAnimation para hacer este trabajo? (Y si es así, ¿por qué?)

Respuesta

13

Parece que hay dos cosas pasando aquí. El primero (como Travis señala correctamente, y la documentación dice) es que las animaciones de UIKit no parecen influir en la animación implícita aplicada a los cambios de propiedad de CALayer. Creo que esto es extraño (UIKit debe usar Core Animation), pero es lo que es.

Aquí hay una solución (posiblemente muy tonto?) Para ese problema:

NSTimeInterval duration = 2.0; // slow things down for ease of debugging 
    [UIView animateWithDuration:duration animations:^{ 
    [CATransaction begin]; 
    [CATransaction setAnimationDuration:duration]; 

    // ... do stuff to things here ... 

    [CATransaction commit]; 
    }]; 

La otra clave es que esta capa es la capa de gradiente de mi vista. Eso significa que mi vista es el delegado de la capa (donde, si la capa de degradado fuera solo una subcapa, no tendría un delegado). Y la implementación UIView de -actionForLayer:forKey: devuelve NSNull para el evento "colors". (Probablemente, cada evento que no esté en una lista específica de animaciones de UIView).)

añadiendo el siguiente código a mi punto de vista hará que el cambio de color a ser animada como se esperaba:

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event 
{ 
    id<CAAction> action = [super actionForLayer:layer forKey:event]; 
    if([@"colors" isEqualToString:event] 
     && (nil == action || (id)[NSNull null] == action)) { 
    action = [CABasicAnimation animationWithKeyPath:event]; 
    } 
    return action; 
} 
+0

¡Fantástico hallazgo! Nunca hubiera adivinado esto. –

1

Debe utilizar CAAnimations explícitos, porque está cambiando el valor de un CALayer. UIViewAnimations trabajan en las propiedades UIView, pero no directamente en las propiedades de su CALayer ...

En realidad, se debe utilizar un CABasicAnimation para que pueda acceder a sus propiedades fromValue y toValue.

El siguiente código debe trabajar para usted:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    [UIView animateWithDuration:2.0f 
          delay:0.0f 
         options:UIViewAnimationCurveEaseInOut 
        animations:^{ 
         CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"colors"]; 
         animation.duration = 2.0f; 
         animation.delegate = self; 
         animation.fromValue = ((CAGradientLayer *)self.layer).colors; 
         animation.toValue = [NSArray arrayWithObjects:(id)[UIColor blackColor].CGColor,(id)[UIColor whiteColor].CGColor,nil]; 
         [self.layer addAnimation:animation forKey:@"animateColors"]; 
        } 
        completion:nil]; 
} 

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 
    NSString *keyPath = ((CAPropertyAnimation *)anim).keyPath; 
    if ([keyPath isEqualToString:@"colors"]) { 
     ((CAGradientLayer *)self.layer).colors = ((CABasicAnimation *)anim).toValue; 
    } 
} 

Hay un truco con CAAnimations en que usted tiene que establecer explícitamente el valor de la propiedad después de que complete la animación.

Para ello, establezca el delegado, en este caso lo configuro para el objeto que llama a la animación y luego anulo su método animationDidStop:finished: para incluir la configuración de los colores del CAGradientLayer en su valor final.

También tendrá que hacer un poco de fundición en el método animationDidStop:, para acceder a las propiedades de la animación.

+0

El caso es que las propiedades CALayer tienen animaciones implícitas. Parece que las propiedades de animación definidas por una animación UIView no se aplican a los cambios de CALayer, pero eso no explica por qué no veo * cualquier * animación del cambio de color. –

+0

Tanto UIView como CALayer tienen animaciones implícitas. Cuando activa una UIViewAnimation y desea activar una animación en su CALayer, debe hacerlo explícitamente al mismo tiempo. Eso es lo que CABasicAnimation hace en las animaciones de UIView:^{} bloque ... –

+0

Pero la clave es que la animación implícita no está sucediendo porque la capa en cuestión es la * capa * de la vista. Esa es una preocupación completamente diferente de si el cambio se realiza dentro de un bloque de animación de UIView. (El objetivo de las animaciones implícitas es que no requieren bloques de animación explícitos.) –

Cuestiones relacionadas