Las properties CAGradientLayer no se animan dentro del bloque de animation UIView

Tengo la sensación de que estoy pasando por alto algo elemental, pero ¿qué mejor manera de encontrarlo que estar equivocado en Internet?

Tengo una UI bastante básica. La vista para mi UIViewController es una subclass cuyo +layerClass es CAGradientLayer . Dependiendo de las acciones del usuario, necesito mover algunos elementos de la interfaz de usuario y cambiar los valores del gradiente de background. El código se ve algo así:

 [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]; }]; 

El problema es que los cambios que hago en este bloque a las subvenciones se animan muy bien (las cosas se mueven y se desvanecen), pero el cambio en los colors del degradado no ocurre. Simplemente intercambia

Ahora, la documentation dice que el código Core Animation dentro de un bloque de animation no henetworkingará las properties del bloque (duración, relajación, etc.). ¿Pero es cierto que eso no define una transacción de animation en absoluto? (La implicación de los documentos parece ser que obtendrá una animation pnetworkingeterminada, donde no obtendré ninguna).

¿Debo usar CAAnimation explícita para que esto funcione? (Y si es así, ¿por qué?)

Parece que hay dos cosas sucediendo aquí. El primero (como bien señala Travis y los estados de la documentation) es que las animaciones UIKit no parecen tener influencia sobre la animation 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 tonta) 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 key es que esta capa de degradado es la capa de mi vista. Eso significa que mi vista es el delegado de la capa (donde, si la capa de gradiente era solo una subcapa, no tendría un delegado). Y la implementación de -actionForLayer:forKey: de -actionForLayer:forKey: devuelve NSNull para el evento "colors" . (Probablemente cada evento que no esté en una list específica de animaciones UIView).

Al agregar el siguiente código a mi vista, el cambio de color se animará como se espera:

 - (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; } 

Tiene que usar CAAnimations explícitas, porque está cambiando el valor de un CALayer. UIViewAnimations funciona en properties UIView, pero no directamente en las properties de su CALayer …

En realidad, debe usar una CABasicAnimation para que pueda acceder a sus properties fromValue y toValue .

El siguiente código debería funcionar 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 tiene que establecer explícitamente el valor de la propiedad DESPUÉS de completar la animation.

Para ello, configura el delegado, en este caso, lo configuro al object que llama a la animation y luego anulo su animationDidStop:finished: método para include la configuration de los colors de CAGradientLayer en su valor final.

También tendrás que hacer un poco de casting en el método animationDidStop: para acceder a las properties de la animation.