Objective-C – CABasicAnimation ¿aplica cambios después de la animación?

Estoy usando CABasicAnimation para mover y cambiar el tamaño de una vista de imagen. Quiero que la vista de la imagen se agregue a la supervista, que sea animada y luego eliminada de la supervista.

Para lograr eso, estoy escuchando una llamada de delegado de mi CAAnimationGroup , y tan pronto como se la llama, elimino la vista de imagen de la supervista.

El problema es que a veces la imagen parpadea en la ubicación inicial antes de ser eliminada de la supervista. ¿Cuál es la mejor manera de evitar este comportamiento?

 CAAnimationGroup *animGroup = [CAAnimationGroup animation]; animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil]; animGroup.duration = .5; animGroup.delegate = self; [imageView.layer addAnimation:animGroup forKey:nil]; 

Cuando agrega una animación a una capa, la animación no cambia las propiedades de la capa. En cambio, el sistema crea una copia de la capa. La capa original se llama capa de modelo y la duplicada se llama capa de presentación. Las propiedades de la capa de presentación cambian a medida que la animación avanza, pero las propiedades de la capa del modelo permanecen sin cambios.

Cuando elimina la animación, el sistema destruye la capa de presentación, dejando solo la capa del modelo, y las propiedades de la capa del modelo controlan cómo se dibuja la capa. Por lo tanto, si las propiedades de la capa del modelo no coinciden con los valores animados finales de las propiedades de la capa de presentación, la capa se restablecerá instantáneamente a su apariencia antes de la animación.

Para solucionar esto, debe establecer las propiedades de la capa del modelo a los valores finales de la animación y luego agregar la animación a la capa. Desea hacerlo en este orden porque cambiar una propiedad de capa puede agregar una animación implícita para la propiedad, lo que entraría en conflicto con la animación que desea agregar explícitamente. Desea asegurarse de que su animación explícita anule la animación implícita.

Entonces, ¿cómo haces todo esto? La receta básica se ve así:

 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.fromValue = [NSValue valueWithCGPoint:myLayer.position]; layer.position = newPosition; // HERE I UPDATE THE MODEL LAYER'S PROPERTY animation.toValue = [NSValue valueWithCGPoint:myLayer.position]; animation.duration = .5; [myLayer addAnimation:animation forKey:animation.keyPath]; 

No he usado un grupo de animación, así que no sé exactamente lo que podrías necesitar cambiar. Solo agrego cada animación por separado a la capa.

También me resulta más fácil usar el método +[CATransaction setCompletionBlock:] para establecer un controlador de finalización para una o varias animaciones, en lugar de intentar usar un delegado de animación. Establece el bloque de finalización de la transacción y luego agrega las animaciones:

 [CATransaction begin]; { [CATransaction setCompletionBlock:^{ [self.imageView removeFromSuperview]; }]; [self addPositionAnimation]; [self addScaleAnimation]; [self addOpacityAnimation]; } [CATransaction commit]; 

CAAnimations se eliminan automáticamente cuando se completa. Hay una propiedad removedOnCompletion que controla esto. Deberías establecer eso en NO .

Además, hay algo conocido como fillMode que controla el comportamiento de la animación antes y después de su duración. Esta es una propiedad declarada en CAMediaTiming (a lo que CAAnimation ajusta). Debe establecer esto en kCAFillModeForwards .

Con estos dos cambios, la animación debería persistir después de que esté completa. Sin embargo, no sé si necesita cambiar estos en el grupo, o en las animaciones individuales dentro del grupo, o en ambos.

Heres es un ejemplo en Swift que puede ayudar a alguien

Es una animación en una capa de degradado. Está animando la propiedad .locations .

El punto crítico como la respuesta @robMayoff explica completamente es que:

Sorprendentemente, cuando haces una animación de capa, ¡en realidad configuras el valor final, primero, antes de comenzar la animación!

El siguiente es un buen ejemplo porque la animación se repite sin fin.

Cuando la animación se repite sin fin, ocasionalmente verá un “destello” entre las animaciones, si comete el clásico error de “olvidarse de establecer el valor antes de animarlo”.

 var previousLocations: [NSNumber] = [] ... func flexTheColors() { // "flex" the color bands randomly let oldValues = previousTargetLocations let newValues = randomLocations() previousTargetLocations = newValues // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!" theLayer.locations = newValues // AND NOW ANIMATE: CATransaction.begin() // and by the way, this is how you endlessly animate: CATransaction.setCompletionBlock{ [weak self] in if self == nil { return } self?.animeFlexColorsEndless() } let a = CABasicAnimation(keyPath: "locations") a.isCumulative = false a.autoreverses = false a.isRemovedOnCompletion = true a.repeatCount = 0 a.fromValue = oldValues a.toValue = newValues a.duration = (2.0...4.0).random() theLayer.add(a, forKey: nil) CATransaction.commit() } 

Lo siguiente puede ayudar a aclarar algo para los nuevos progtwigdores. Tenga en cuenta que en mi código hago esto:

  // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!" theLayer.locations = newValues // AND NOW ANIMATE: CATransaction.begin() ...set up the animation... CATransaction.commit() 

Sin embargo, en el ejemplo del código en la otra respuesta, es así:

  CATransaction.begin() ...set up the animation... // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!" theLayer.locations = newValues CATransaction.commit() 

En cuanto a la posición de la línea de código donde “estableces los valores, ¡antes de animar!” ..

En realidad, está perfectamente bien tener esa línea realmente “dentro” de las líneas de código de inicio y compromiso. Siempre que lo hagas antes de .commit() .

Solo lo menciono, ya que puede confundir a los nuevos animadores.