framework - swift ios documentation
CABasicAnimation no se anima correctamente cuando actualizo la capa del modelo (1)
Creo que es más fácil explicar lo que está sucediendo para cada una de las tres ubicaciones y luego una "conclusión" al final.
También estoy agregando algunas ilustraciones, que muestran exactamente el comportamiento que usted menciona en su pregunta para que sea más fácil de seguir para alguien que no ha probado estas tres cosas por sí mismo. También estoy extendiendo la ilustración para mostrar tanto una capa independiente como una capa de respaldo (una que está unida a una vista) y explicaré la diferencia donde hay una.
Ubicación 1
En la primera ubicación, el valor del modelo se actualiza antes de que se cree la animación. Una vez hecho esto, la propiedad transform contiene la actualizaciónTransform. Esto significa que cuando lee la transformación de la capa para fromValue, obtiene el valor actualizado de nuevo. Esto a su vez significa que tanto para como para el valor son los mismos para que no pueda ver la animación.
Una cosa que podría haber hecho que esta ubicación funcione como se espera es leer el valor anterior antes de asignar el nuevo valor y luego usarlo como fromValue. 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 lee la transformación para el valor desde, por lo que fromValue y toValue son diferentes. Después de eso, el valor del modelo se actualiza a su valor final. En realidad, existe 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 realizará automáticamente una animación "implícita" cuando el valor cambie. Esto significa que se agregará una animación a la capa para la ruta de la tecla "transformar". La vista, sin embargo, deshabilita este comportamiento cuando el cambio ocurre fuera de un bloque de animación, por lo que no hay animación implícita allí.
La razón por la que no vemos la animación implícita es que la animación "explícita" se agrega después para la misma ruta de clave. Esto significa que la única animación explícita será visible, en ambos casos, incluso si hay dos animaciones ejecutándose en la capa independiente (más sobre esto más adelante). Si se siente prudente, puede desactivar la acción implícita para la capa independiente (más sobre esto más adelante).
Ubicación 3
Esto nos deja con la última ubicación. En este caso, la animación se crea igual que arriba, con valores diferentes de Valor y valor. La única diferencia es el orden de agregar la animación explícita y cambiar la propiedad que desencadena una animación implícita. En este caso, la animación implícita se agrega después de la animación explícita y ambos se ejecutan (!). Ambas animaciones se ejecutaron para la ubicación 2, pero no pudimos verla porque la animación explícita (más larga) se agregó antes.
Como todo se mueve tan rápido, desaceleré toda la capa para tratar de ilustrar lo que sucede cuando dos animaciones se ejecutan a la vez. De esta forma, es mucho más fácil ver qué sucede cuando termina la animación implícita. He superpuesto la capa de respaldo que se comporta bien y la capa independiente que funciona mal y los he hecho transparentes al 50%. El contorno discontinuo es el marco original.
Una breve descripción de lo que está sucediendo: la vista azul obtiene solo la animación explícita añadida (que tiene una duración de 1 segundo). La capa naranja primero tiene la misma animación explícita añadida y luego se le agrega una animación implícita de 0,25 segundos. Ni las animaciones explícitas ni las implícitas son "aditivas", lo que significa que su valor y valor se utilizan tal como están.
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.
A mi entender (ver descargo de responsabilidad) esto es lo que ocurre para cada actualización de pantalla para producir la animación: para la marca de tiempo actual, la capa pasa por las animaciones en el orden en que se agregaron y actualiza los valores de presentación. En este caso, la animación explícita establece una transformación de rotación, luego viene la animación implícita y establece otra transformación de rotación que anula por completo la transformación explícita.
Si una animación está configurada para ser "aditiva", se agregará a los valores de presentación en lugar de sobrescribir (que es súper potente). Incluso con animaciones aditivas, el orden sigue siendo importante. Una animación no aditiva podría venir más tarde y sobrescribir todo.
Dado que la animación implícita es más corta que la explícita, vemos que para la primera parte de la animación total, los valores proceden estrictamente de la animación implícita (que se agregó en último lugar). Una vez que termina la animación implícita, la única animación restante es la animación explícita que se ha estado ejecutando debajo del implícito, todo este tiempo. Entonces, cuando termina la animación implícita, la animación explícita ya ha progresado 0,25 segundos y vemos que la capa naranja salta de nuevo al mismo valor que la vista azul, en lugar de retroceder 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 ubicación donde se actualiza el valor no impide que haya dos animaciones (pero puede afectar la apariencia del resultado final).
Para evitar que se agreguen dos acciones a la capa independiente, desactivamos temporalmente todas las "acciones" (un término más general para una animación):
[CATransaction begin];
[CATransaction setDisableActions:YES]; // actions are disabled for now
layer.transform = updatedTransform;
[CATransaction commit]; // until here
Cuando hacemos esto, solo se agrega una animación a la capa para que funcione la ubicación 2 o 3. Eso es simplemente una cuestión de gusto. Si lee el valor anterior, también puede usar la ubicación 1 (siempre que la acción esté desactivada).
Si estás animando una capa de respaldo, entonces no tienes que deshabilitar las acciones (la vista lo hace por ti), pero tampoco te duele hacerlo.
En este punto, podría continuar con otras formas de configurar una animación, qué es una animación aditiva y por qué necesitaba especificar tanto el valor de salida como el valor deValor en este caso. Pero creo que he respondido la pregunta que me hiciste y que esta respuesta ya es un poco larga.
Actualmente estoy implementando una CABasicAnimation
que anima una propiedad de transform
CALayer
. Ahora, aunque soy nuevo en Core Animation, he podido reunir 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 utilizando el fillMode
y removedOnCompletion
propiedades de una animación. Muchos consideran que este método es una mala práctica porque crea una discrepancia entre la capa del modelo y la capa de presentación, por lo que las consultas futuras a una de esas capas pueden no coincidir con lo que el usuario está viendo.
En cambio, la forma recomendada de hacer animaciones es actualizar la capa del modelo al mismo tiempo que agrega la animación a la capa que se está animando. Sin embargo, tengo problemas para entender exactamente cómo está funcionando. Mi animación es simple y dice 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 modelo utilizando el código
self.layerBeingAnimated.transform = updatedTransform;
En la ubicación 1, la capa salta directamente a newTransform
y no se anima. En la ubicación 2, la capa se anima exactamente como yo quiero desde la transformación actual a newTransform
. En la ubicación 3, la capa salta a la derecha a newTransform
, salta a la antigua transformación, se anima correctamente desde fromValue a newTransform, y luego permanece en newTransform
.
¿Cuál es el trato aquí? ¿Cuál es la ubicación correcta para actualizar la capa del modelo y por qué estas tres ubicaciones producen resultados tan diferentes?
¡Gracias!