CABasicAnimation no se anima correctamente cuando actualizo la capa del model

Actualmente estoy implementando una CABasicAnimation que anima una propiedad de transform CALayer . Ahora, aunque soy nuevo en Core Animation, he podido recostackr a través de varios blogs y artículos como objc.io que es una muy mala idea usar el método a menudo (incorrectamente) recomendado para hacer que las animaciones se peguen usando el fillMode y removedOnCompletion de una animation. Muchos consideran que este método es una mala práctica porque crea una discrepancia entre la capa del model y la capa de presentación, por lo que las consultas futuras a una de esas capas pueden no coincidir con lo que ve el usuario.

En cambio, la forma recomendada de hacer animaciones es actualizar la capa del model al mismo time que agrega la animation a la capa que se está animando. Sin embargo, estoy teniendo problemas para entender exactamente cómo funciona esto. Mi animation es simple y va así:

 CATransform3D updatedTransform = [self newTransformWithCurrentTransform]; // Location 1 CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"]; transformAnimation.duration = 1; transformAnimation.fromValue = [NSValue valueWithCATransform3D:self.layerBeingAnimated.transform]; // Does not work without this. transformAnimation.toValue = [NSValue valueWithCATransform3D:updatedTransform]; // Location 2 [self.layerBeingAnimated addAnimation:transformAnimation forKey:kTransformAnimationKey]; // Location 3 

He indicado tres ubicaciones en las que he intentado actualizar la capa del model usando el código

 self.layerBeingAnimated.transform = updatedTransform; 

En la location 1, la capa salta directamente a newTransform y no se anima. En la location 2, la capa se anima exactamente como lo desee desde la transformación actual a newTransform . En la location 3, la capa salta directamente a newTransform , vuelve a la transformación anterior, se anima correctamente desde theValue a newTransform y luego se queda en newTransform .

¿Cuál es el problema aquí? ¿Cuál es la location correcta para actualizar la capa del model y por qué estas tres ubicaciones producen resultados tan diferentes?

¡Gracias!

Creo que es más fácil explicar lo que sucede para cada uno de los tres lugares y luego una "conclusión" al final.

También estoy agregando algunas ilustraciones, mostrando exactamente el comportamiento que menciona en su pregunta para que sea más fácil de seguir para alguien que no haya probado estas tres cosas por sí mismo. También extiendo la ilustración para mostrar tanto una capa independiente como una capa de respaldo (una que está adjunta a una vista) y explicaré la diferencia donde la hay.

Ubicación 1

En la primera location, el valor del model se actualiza antes de crear la animation. Una vez hecho esto, la propiedad de transformación contiene el updateTransform. Esto significa que cuando lees la transformación de la capa para el valor de fromValue, recuperas el valor actualizado. Esto a su vez significa que tanto para como para el valor son los mismos, por lo que no puede ver la animation.

introduzca la descripción de la imagen aquí

Una cosa que podría haber hecho que esta location funcione como se espera es leer el valor anterior antes de asignar el nuevo valor y luego usar eso como valor de. Esto se verá como se esperaba.

 // Location 1 CATransform3D oldValue = layer.transform; // read the old value first layer.transform = updatedTransform; // then update to the new value CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"]; anim.duration = 1.0; anim.fromValue = [NSValue valueWithCATransform3D:oldValue]; anim.toValue = [NSValue valueWithCATransform3D:updatedTransform]; 

Ubicación 2

En el segundo ejemplo, el valor aún no se actualiza cuando se lee la transformación para el valor de, por lo que los valores fromValue y toValue son diferentes. Después de eso, el valor del model se actualiza a su valor final. En realidad, hay una diferencia entre la capa independiente y la capa de respaldo aquí, pero no la vemos. La propiedad de transform en CALayer es animable y automáticamente realizará una animation "implícita" cuando el valor cambie. Esto significa que se agregará una animation a la capa para la ruta de la key "transformar". La vista, sin embargo, deshabilita este comportamiento cuando el cambio ocurre fuera de un bloque de animation, por lo que no hay animation implícita allí.

La razón por la que no vemos la animation implícita es que luego se agrega la animation "explícita" para la misma ruta de acceso key. Esto significa que la única animation explícita será visible, en ambos casos, incluso si hay dos animaciones ejecutándose en la capa independiente (más sobre eso más adelante). Si se siente cauteloso, puede deshabilitar la acción implícita para la capa independiente (más sobre eso más adelante).

introduzca la descripción de la imagen aquí

Ubicación 3

Esto nos deja con la última location. En este caso, la animation se crea igual que la anterior, con diferente de valor y valor. La única diferencia es el order de agregar la animation explícita y cambiar la propiedad que activa una animation implícita. En este caso, la animation implícita se agrega después de la animation explícita y ambos ejecutan (!). Ambas animaciones funcionaron para la location 2, pero no pudimos verla porque la animation explícita (más larga) se agregó después.

introduzca la descripción de la imagen aquí

Dado que todo se mueve tan rápido, networkinguje la velocidad de toda la capa para intentar ilustrar lo que sucede cuando se ejecutan dos animaciones al mismo time. De esta manera, resulta mucho más fácil ver qué sucede cuando termina la animation implícita. He superpuesto a la capa de soporte que se comporta bien y la capa independiente que se comporta mal y los he hecho 50% transparentes. El contorno discontinuo es el marco original.

introduzca la descripción de la imagen aquí

Una breve descripción de lo que está sucediendo: la vista azul obtiene solo la animation explícita agregada (que tiene una duración de 1 segundo). La capa naranja primero tiene la misma animation de exploit que se agregó y luego se le agrega una animation implícita de 0.25 segundos. Ni las animaciones explícitas ni las implícitas son "aditivas", lo que significa que su valor de valor y valor de valor se utilizan tal cual.

Descargo de responsabilidad: no trabajo en Apple y no he visto el código fuente de Core Animation, así que lo que voy a decir es conjeturas basadas en cómo se comportan las cosas.

En mi opinión (vea la exención de responsabilidad) esto es lo que sucede para cada actualización de pantalla para producir la animation: para la timestamp actual, la capa pasa a través de las animaciones en el order en que se agregaron y actualiza los valores de presentación. En este caso, la animation explícita establece una transformación de rotation, luego viene la animation implícita y establece otra transformación de rotation que anula por completo la transformación explícita.

Si una animation está configurada para ser "aditiva", se agregará a los valores de presentación en lugar de sobrescribir (que es súper poderosa). Incluso con animaciones aditivas, el order sigue siendo importante. Una animation no aditiva podría venir más tarde y sobrescribir todo el asunto.

Dado que la animation implícita es más corta que la explícita, vemos que para la primera parte de la animation total, los valores provienen estrictamente de la animation implícita (que se agregó en último lugar). Una vez que finaliza la animation implícita, la única animation restante es la animation explícita que se ha ejecutado debajo de la implícita, todo este time. Entonces, cuando la animation implícita termina, la animation explícita ya ha progresado 0.25 segundos y vemos que la capa naranja vuelve al mismo valor que la vista azul, en lugar de saltar al principio.

¿Dónde deberíamos actualizar el valor?

En este punto, la pregunta es, ¿cómo podemos evitar que se agreguen dos animaciones y dónde debemos actualizar el valor? La location donde se actualiza el valor no evita que haya dos animaciones (pero puede afectar la apariencia del resultado final).

Para evitar que dos acciones se agreguen a la capa independiente, deshabilitamos temporalmente todas las "acciones" (un término más general para una animation):

 [CATransaction begin]; [CATransaction setDisableActions:YES]; // actions are disabled for now layer.transform = updatedTransform; [CATransaction commit]; // until here 

Cuando hacemos esto, solo se agrega una animation a la capa, de modo que la location 2 o 3 funciona. Eso es simplemente cuestión de gusto. Si lees el antiguo valor, también puedes usar la location 1 (siempre que la acción esté deshabilitada).

Si está animando una capa de respaldo, no tiene que deshabilitar las acciones (la vista lo hace por usted), pero tampoco duele hacerlo.


En este punto, podría continuar con otras forms de configurar una animation, lo que es una animation aditiva y por qué debes especificar tanto el valor de valor como valor de valor en este caso. Pero creo que he respondido la pregunta que le hiciste y que esta respuesta ya es un poco larga.