Tuve un problema en el que tenía una serie de secuencias CATransition / CAAnimation superpuestas, todas las cuales necesitaba realizar operaciones personalizadas cuando las animaciones se detenían, pero solo quería un controlador delegado para animationDidStop.
Sin embargo, tuve un problema, no parecía haber una forma de identificar de forma única cada CATransition / CAAnimation en el delegado animationDidStop.
Resolví este problema a través del sistema clave / valor expuesto como parte de CAAnimation.
Cuando comiences tu animación, usa el método setValue en CATransition / CAAnimation para configurar tus identificadores y valores para usar cuando animationDidStop se active:
-(void)volumeControlFadeToOrange
{
CATransition* volumeControlAnimation = [CATransition animation];
[volumeControlAnimation setType:kCATransitionFade];
[volumeControlAnimation setSubtype:kCATransitionFromTop];
[volumeControlAnimation setDelegate:self];
[volumeControlLevel setBackgroundImage:[UIImage imageNamed:@"SpecialVolume1.png"] forState:UIControlStateNormal];
volumeControlLevel.enabled = true;
[volumeControlAnimation setDuration:0.7];
[volumeControlAnimation setValue:@"Special1" forKey:@"MyAnimationType"];
[[volumeControlLevel layer] addAnimation:volumeControlAnimation forKey:nil];
}
- (void)throbUp
{
doThrobUp = true;
CATransition *animation = [CATransition animation];
[animation setType:kCATransitionFade];
[animation setSubtype:kCATransitionFromTop];
[animation setDelegate:self];
[hearingAidHalo setBackgroundImage:[UIImage imageNamed:@"m13_grayglow.png"] forState:UIControlStateNormal];
[animation setDuration:2.0];
[animation setValue:@"Throb" forKey:@"MyAnimationType"];
[[hearingAidHalo layer] addAnimation:animation forKey:nil];
}
En su delegado animationDidStop:
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag{
NSString* value = [theAnimation valueForKey:@"MyAnimationType"];
if ([value isEqualToString:@"Throb"])
{
//... Your code here ...
return;
}
if ([value isEqualToString:@"Special1"])
{
//... Your code here ...
return;
}
//Add any future keyed animation operations when the animations are stopped.
}
El otro aspecto de esto es que le permite mantener el estado en el sistema de emparejamiento de valor clave en lugar de tener que almacenarlo en su clase delegada. Cuanto menos código, mejor.
Asegúrese de consultar la Referencia de Apple sobre codificación de pares de valores clave .
¿Existen mejores técnicas para la identificación de CAAnimation / CATransition en el delegado animationDidStop?
Gracias, - Batgar
fuente
CAAnimation
'sdelegate
es fuerte, por lo que puede que tenga que configurarlo paranil
evitar retener ciclos!Respuestas:
La técnica de Batgar es demasiado complicada. ¿Por qué no aprovechar el parámetro forKey en addAnimation? Estaba destinado a este mismo propósito. Simplemente saque la llamada a setValue y mueva la cadena de teclas a la llamada addAnimation. Por ejemplo:
Luego, en su devolución de llamada animationDidStop, puede hacer algo como:
fuente
anim.removedOnCompletion = NO;
para que aún exista cuando-animationDidStop:finished:
se llame.Se me ocurrió una forma aún mejor de hacer el código de finalización para CAAnimations:
Creé un typedef para un bloque:
Y una clave que uso para agregar un bloque a una animación:
Luego, si quiero ejecutar el código de finalización de la animación después de que finalice una CAAnimation, me establezco como delegado de la animación y agrego un bloque de código a la animación usando setValue: forKey:
Luego, implemento un método animationDidStop: finalizado:, que busca un bloque en la clave especificada y lo ejecuta si lo encuentra:
La belleza de este enfoque es que puede escribir el código de limpieza en el mismo lugar donde crea el objeto de animación. Mejor aún, dado que el código es un bloque, tiene acceso a las variables locales en el ámbito adjunto en el que está definido. No tiene que meterse con la configuración de diccionarios de userInfo u otras tonterías, y no tiene que escribir un método animationDidStop: finalizado: cada vez mayor que se vuelve cada vez más complejo a medida que agrega diferentes tipos de animaciones.
A decir verdad, CAAnimation debería tener una propiedad de bloque de finalización incorporada y soporte del sistema para llamarlo automáticamente si se especifica uno. Sin embargo, el código anterior le brinda la misma funcionalidad con solo unas pocas líneas de código adicional.
fuente
theBlock();
se invoca, y creo que se debe al hecho de que se destruyó el alcance del bloque.El segundo enfoque solo funcionará si estableces explícitamente que tu animación no se elimine al finalizar antes de ejecutarla:
Si no lo hace, su animación se eliminará antes de que se complete y la devolución de llamada no la encontrará en el diccionario.
fuente
¡Todas las demás respuestas son demasiado complicadas! ¿Por qué no agrega su propia clave para identificar la animación?
Esta solución es muy fácil, todo lo que necesita es agregar su propia clave a la animación (animationID en este ejemplo)
Inserte esta línea para identificar animation1 :
y esto para identificar animation2 :
Pruébelo así:
No requiere ninguna variable de instancia :
fuente
[animation valueForKey:@"animationID"]
Para hacer explícito lo que está implícito desde arriba (y lo que me trajo aquí después de algunas horas desperdiciadas): no espere ver el objeto de animación original que asignó.
cuando termina la animación, porque
[CALayer addAnimation:forKey:]
hace una copia de tu animación.En lo que puede confiar es en que los valores con clave que le dio a su objeto de animación todavía están allí con un valor equivalente (pero no necesariamente equivalencia de puntero) en el objeto de animación de réplica pasado con el
animationDidStop:finished:
mensaje. Como se mencionó anteriormente, use KVC y obtendrá un amplio margen para almacenar y recuperar el estado.fuente
[animation setValue:@"myanim" forKey:@"name"]
e incluso puede configurar la capa que se está animando usando[animation setValue:layer forKey:@"layer"]
. Luego, estos valores se pueden recuperar dentro de los métodos delegados.valueForKey:
vuelvenil
por mí, ¿alguna idea de por qué?Puedo ver la mayoría de las respuestas objc. Haré una para swift 2.3 según la mejor respuesta anterior.
Para empezar, será bueno almacenar todas esas claves en una estructura privada para que sea seguro de tipos y cambiarlo en el futuro no le traerá errores molestos solo porque olvidó cambiarlo en todas partes del código:
Como puede ver he cambiado los nombres de las variables / animaciones para que quede más claro. Ahora establezca estas claves cuando se crea la animación.
(...)
Luego, finalmente manejando al delegado para cuando la animación se detenga.
fuente
En mi humilde opinión, usar el valor clave de Apple es la forma elegante de hacer esto: está específicamente diseñado para permitir agregar datos específicos de la aplicación a los objetos.
Otra posibilidad mucho menos elegante es almacenar referencias a sus objetos de animación y hacer una comparación de punteros para identificarlos.
fuente
Para verificar si 2 objetos CABasicAnimation son la misma animación, uso la función keyPath para hacer exactamente eso.
if ([animationA keyPath] == [animationB keyPath])
fuente
Me gusta usar
setValue:forKey
: para mantener una referencia de la vista que estoy animando, es más seguro que intentar identificar de manera única la animación basada en la ID porque el mismo tipo de animación se puede agregar a diferentes capas.Estos dos son equivalentes:
Con este:
y en el método delegado:
fuente
Xcode 9 Swift 4.0
Puede usar valores clave para relacionar una animación que agregó a la animación devuelta en el método delegado animationDidStop.
Declare que un diccionario contiene todas las animaciones activas y terminaciones relacionadas:
Cuando agregue su animación, establezca una clave para ella:
En animationDidStop, ocurre la magia:
fuente