core-animation caanimation cabasicanimation

core animation - Cómo encadenar CAAnimation diferente en una aplicación iOS



core-animation cabasicanimation (5)

Necesito encadenar animaciones, CABasicAnimation o CAAnimationGroup pero no sé cómo hacerlo, lo único que hago es que toda la animación se ejecute al mismo tiempo para la misma capa.

¿Cómo podría hacerlo?

Por ejemplo, una capa con su contenido establecido en una imagen de automóvil:

1º: mueve los puntos X a la derecha

2º: girar 90ª a la izquierda.

3º: Mueve el punto X

4º: escalar la capa

Todas estas animaciones deben ejecutarse de forma secuencial, pero no puedo hacerlo: S

BTW: No soy inglés, lo siento si cometí algunos errores en mi gramática: D


Aquí hay una solución en Swift:

var animations = [CABasicAnimation]() var animation1 = CABasicAnimation(keyPath: "key_path_1") // animation set up here, I''ve included a few properties as an example animation1.duration = 1.0 animation1.fromValue = 1 animation1.toValue = 0 animation1.append(animation1) var animation2 = CABasicAnimation(keyPath: "key_path_2") animation2.duration = 1.0 animation2.fromValue = 1 animation2.toValue = 0 // setting beginTime is the key to chaining these animation2.beginTime = 1.0 animations.append(animation2) let group = CAAnimationGroup() group.duration = 2.0 group.repeatCount = FLT_MAX group.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) group.animations = animations yourLayer.addAnimation(group, forKey: nil)


Lo que David sugiere funciona bien, pero lo recomendaría de otra manera.

Si todas sus animaciones son de la misma capa, puede crear un grupo de animaciones y hacer que cada animación tenga un tiempo de beginTime diferente, donde la primera animación comienza en el tiempo de beginTime 0, y cada nueva animación comienza después de la duración total de las animaciones anteriores.

Sin embargo, si sus animaciones están en capas diferentes, no puede usar grupos de animación (todas las animaciones en un grupo de animación deben actuar en la misma capa). En ese caso, debe enviar animaciones separadas, cada una de las cuales tiene un tiempo de beginTime que se compensa con CACurrentMediaTime() , por ejemplo:

CGFloat totalDuration = 0; CABasicAnimation *animationOne = [CABasicAnimation animationWithKeyPath: @"alpha"]; animationOne.beginTime = CACurrentMediaTime(); //Start instantly. animationOne.duration = animationOneDuration; ... //add animation to layer totalDuration += animationOneDuration; CABasicAnimation *animationTwo = [CABasicAnimation animationWithKeyPath: @"position"]; animationTwo.beginTime = CACurrentMediaTime() + totalDuration; //Start after animation one. animationTwo.duration = animationTwoDuration; ... //add animation to layer totalDuration += animationTwoDuration; CABasicAnimation *animationThree = [CABasicAnimation animationWithKeyPath: @"position"]; animationThree.beginTime = CACurrentMediaTime() + totalDuration; //Start after animation three. animationThree.duration = animationThreeDuration; ... //add animation to layer totalDuration += animationThreeDuration;



Utilice KVC. setValue for Key para cada animación. Después de eso, en animationDidStop puede definir qué animación se ha detenido y ejecutar a continuación en cadena.

- (void)moveXrightAnimation { CABasicAnimation* theAnimation = ... [theAnimation setValue:@"movexright" forKey:@"animationID"]; //animation } - (void)rotate90leftAnimation { CABasicAnimation* theAnimation = ... [theAnimation setValue:@"rotate90left" forKey:@"animationID"]; //animation } - (void)moveXpointAnimation { CABasicAnimation* theAnimation = ... [theAnimation setValue:@"movexpoint" forKey:@"animationID"]; //animation } - (void)scaleAnimation { CABasicAnimation* theAnimation = ... [theAnimation setValue:@"scale" forKey:@"animationID"]; //animation } #pragma mark - CAAnimationDelegate - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { if([[anim valueForKey:@"animationID"] isEqual:@"movexright"]) { [self rotate90leftAnimation]; } if([[anim valueForKey:@"animationID"] isEqual:@"rotate90left"]) { [self moveXpointAnimation]; } if([[anim valueForKey:@"animationID"] isEqual:@"movexpoint"]) { [self scaleAnimation]; } }


tl; dr: debe agregar manualmente cada animación después de los acabados anteriores.

No hay una forma integrada para agregar animaciones secuenciales. Puede establecer que el retraso de cada animación sea la suma de todas las animaciones anteriores, pero no lo recomendaría.

En su lugar, crearía todas las animaciones y las agregaría a una matriz mutable (usando la matriz como una cola) en el orden en que se supone que deben ejecutarse. Luego, al configurarse como delegado de animaciones para todas las animaciones, puede obtener la animationDidStop:finished: devolución de llamada cada vez que finaliza una animación.

En ese método, quitará la primera animación (es decir, la siguiente animación) de la matriz y la agregará a la capa. Ya que eres el delegado, obtendrás una segunda animación cuando finalice esa, en cuyo caso la animationDidStop:finished: devolución de llamada se ejecutará de nuevo y la siguiente animación se eliminará de la matriz mutable y se agregará a la capa.

Una vez que el conjunto de animaciones esté vacío, todas las animaciones se habrán ejecutado.

Algún código de ejemplo para comenzar. Primero configuras todas tus animaciones:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"]; [animation setToValue:(id)[[UIColor redColor] CGColor]]; [animation setDuration:1.5]; [animation setDelegate:self]; [animation setValue:[view layer] forKey:@"layerToApplyAnimationTo"]; // Configure other animations the same way ... [self setSequenceOfAnimations:[NSMutableArray arrayWithArray: @[ animation, animation1, animation2, animation3, animation4, animation5 ] ]]; // Start the chain of animations by adding the "next" (the first) animation [self applyNextAnimation];

Luego, en la devolución de llamada de delegado, simplemente vuelva a aplicar la siguiente animación.

- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished { [self applyNextAnimation]; } - (void)applyNextAnimation { // Finish when there are no more animations to run if ([[self sequenceOfAnimations] count] == 0) return; // Get the next animation and remove it from the "queue" CAPropertyAnimation * nextAnimation = [[self sequenceOfAnimations] objectAtIndex:0]; [[self sequenceOfAnimations] removeObjectAtIndex:0]; // Get the layer and apply the animation CALayer *layerToAnimate = [nextAnimation valueForKey:@"layerToApplyAnimationTo"]; [layerToAnimate addAnimation:nextAnimation forKey:nil]; }

Estoy usando una clave personalizada layerToApplyAnimationTo para que cada animación conozca su capa (funciona solo con setValue:forKey: y valueForKey: .