Las llamadas consecutivas de animation no funcionan

Tengo un button que llama a un código animateWithDuration que desvanece una image, desactiva el text y un nuevo color bg y luego vuelve a normalizarse. La animation tarda unos segundos en completarse y funciona muy bien.

¡Sin embargo! Hay un problema:

A veces, este button se presionará nuevamente antes de que la animation finalice. Cuando esto sucede, quiero que el animado actual se detenga y comience de nuevo.

La solución investigada no funciona

De acuerdo con mi lectura, la solución debería ser simple, solo importa QuartzCore y agrega:

button.layer.removeAllAnimations() 

Esto elimina la animation, pero la nueva / segunda animation está totalmente desorderada. La image que se supone que está oculta no es, el text nunca aparece, y la transición de color es incorrecta. ¡¿¡Que está pasando!?!

 //Animate Finished feedback in footer bar func animateFinished(textToDisplay: String, footerBtn: UIButton, footerImg: UIImageView) { //Should cancel any current animation footerBtn.layer.removeAllAnimations() footerBtn.alpha = 0 footerBtn.setTitle(textToDisplay, forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Regular", size: 18) footerBtn.setTitleColor(UIColor(networking: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 1.0), forState: UIControlState.Normal) footerBtn.backgroundColor = UIColor(networking: 217/255.0, green: 217/255.0, blue: 217/255.0, alpha: 1.0) UIView.animateWithDuration(0.5, delay: 0.0, options: nil, animations: { footerImg.alpha = 0.01 //Img fades out footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 163/255.0, blue: 00/255.0, alpha: 0.6) } , completion: { finished in UIView.animateWithDuration(0.5, delay: 0.0, options: nil, animations: { footerBtn.alpha = 1 //Text fades in footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 208/255.0, blue: 11/255.0, alpha: 0.6) } , completion: { finished in UIView.animateWithDuration(0.5, delay: 1.0, options: nil, animations: { footerBtn.alpha = 0.01 //Text fades out footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 173/255.0, blue: 00/255.0, alpha: 0.6) } , completion: { finished in UIView.animateWithDuration(0.5, delay: 0.0, options: nil, animations: { footerImg.alpha = 1 //Img fades in } , completion: { finished in footerBtn.backgroundColor = UIColor.clearColor() footerBtn.setTitleColor(UIColor(networking: 55/255.0, green: 55/255.0, blue: 55/255.0, alpha: 1.0), forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Light", size: 18) footerBtn.setTitle("", forState: UIControlState.Normal) footerBtn.alpha = 1 //Completion blocks sets values back to norm }) }) }) }) }//End of animation 

@Shripada sugirió cambiar a fotogtwigs key para get un código más legible. Formato de fotogtwigs key a continuación. No solucionó el problema de interrupción de la animation. Si puede resolver el problema en formatting nested o fotogtwig key, ¡publíquelo!

 func animateFinished(textToDisplay: String, footerBtn: UIButton, footerImg: UIImageView) { //Should cancel any current animation footerBtn.layer.removeAllAnimations() footerBtn.alpha = 0 footerBtn.setTitle(textToDisplay, forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Regular", size: 18) footerBtn.setTitleColor(UIColor(networking: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 1.0), forState: UIControlState.Normal) //footerBtn.backgroundColor = UIColor(networking: 217/255.0, green: 217/255.0, blue: 217/255.0, alpha: 1.0) UIView.animateKeyframesWithDuration(3.0 /*Total*/, delay:0.0, options: UIViewKeyframeAnimationOptions.CalculationModeLinear, animations: { UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration:0.10, animations:{ footerImg.alpha = 0.01 //Img fades out footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 103/255.0, blue: 00/255.0, alpha: 0.6) //Bg turns to green }) UIView.addKeyframeWithRelativeStartTime(0.10, relativeDuration:0.30, animations:{ footerBtn.alpha = 1 //Text and green bg fades in footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 173/255.0, blue: 11/255.0, alpha: 0.6) //BG turns greener }) UIView.addKeyframeWithRelativeStartTime(0.40, relativeDuration:0.50, animations:{ footerBtn.alpha = 0.01 //Text fades out & bg fade out }) }, completion: { finished in footerImg.alpha = 1 footerBtn.alpha = 1 footerBtn.backgroundColor = UIColor.clearColor() footerBtn.setTitleColor(UIColor(networking: 55/255.0, green: 55/255.0, blue: 55/255.0, alpha: 1.0), forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Light", size: 18) footerBtn.setTitle("", forState: UIControlState.Normal) //Completion blocks sets values back to norm } ) }//End of 'Finished' animation 

animateFinished algunas líneas de print a su método animateFinished , para ver lo que está sucediendo:

 func animateFinished(textToDisplay: String, footerBtn: UIButton, footerImg: UIImageView) { //Should cancel any current animation print("Remove animations") footerBtn.layer.removeAllAnimations() print("Animations removed") footerBtn.alpha = 0 footerBtn.setTitle(textToDisplay, forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Regular", size: 18) footerBtn.setTitleColor(UIColor(networking: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 1.0), forState: UIControlState.Normal) //footerBtn.backgroundColor = UIColor(networking: 217/255.0, green: 217/255.0, blue: 217/255.0, alpha: 1.0) print("Initial animation setup completed") UIView.animateKeyframesWithDuration(3.0 /*Total*/, delay:0.0, options: UIViewKeyframeAnimationOptions.CalculationModeLinear, animations: { UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration:0.10, animations:{ footerImg.alpha = 0.01 //Img fades out footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 103/255.0, blue: 00/255.0, alpha: 0.6) //Bg turns to green }) UIView.addKeyframeWithRelativeStartTime(0.10, relativeDuration:0.30, animations:{ footerBtn.alpha = 1 //Text and green bg fades in footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 173/255.0, blue: 11/255.0, alpha: 0.6) //BG turns greener }) UIView.addKeyframeWithRelativeStartTime(0.40, relativeDuration:0.50, animations:{ footerBtn.alpha = 0.01 //Text fades out & bg fade out }) }, completion: { finished in print("Completion block started") footerImg.alpha = 1 footerBtn.alpha = 1 footerBtn.backgroundColor = UIColor.clearColor() footerBtn.setTitleColor(UIColor(networking: 55/255.0, green: 55/255.0, blue: 55/255.0, alpha: 1.0), forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Light", size: 18) footerBtn.setTitle("", forState: UIControlState.Normal) //Completion blocks sets values back to norm print("Completion block finished") } ) }//End of 'Finished' animation 

Si permite que las animaciones se ejecuten hasta completarse, el logging muestra, como era de esperar:

 Remove animations Animations removed Initial animation setup completed Completion block started Completion block finished 

Pero si tocas el button durante la animation, verás esto:

 Remove animations Animations removed Initial animation setup completed Remove animations Animations removed Initial animation setup completed Completion block started Completion block finished Completion block started Completion block finished 

Lo que sucede es que removeAllAnimations hace que el bloque de finalización (para la primera llamada) se ejecute, después de que se haya completado la configuration inicial para la segunda llamada, pero antes de que se realicen las segundas animaciones. Entonces, por ejemplo, el título del button es "" durante la segunda animation.

La corrección es relativamente sencilla: no ejecute el bloque de finalización si las animaciones no han finalizado:

  completion: { finished in if (!finished) { return } print("Completion block started") footerImg.alpha = 1 footerBtn.alpha = 1 footerBtn.backgroundColor = UIColor.clearColor() footerBtn.setTitleColor(UIColor(networking: 55/255.0, green: 55/255.0, blue: 55/255.0, alpha: 1.0), forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Light", size: 18) footerBtn.setTitle("", forState: UIControlState.Normal) print("Completion block finished") //Completion blocks sets values back to norm } 

Además, según Shripada, deberás eliminar las animaciones de footerImg y footerBtn, con:

 footerImg.layer.removeAllAnimations() 

Al comienzo del método.

Debe evitar la secuencia de sus animaciones en este tipo de anidamiento utilizando los bloques de finalización de animaciones sucesivas. Esto no solo lo hace altamente ilegible, sino que también dificulta la comprensión y resolución de problemas como el que usted menciona.

Hay una alternativa mucho mejor, llamada animaciones de fotogtwigs key, y deberías considerar usarlo (disponible iOS 7 en adelante).

 animateKeyFramesWithDuration:delay:options:animation:completion 

Consulte la documentation

Su código de animation puede ser reescrito usando fotogtwigs key (PS: no lo he probado, simplemente escribiéndolo para su reference)

 func animateFinished(textToDisplay: String, footerBtn: UIButton, footerImg: UIImageView) { //Should cancel any current animation footerBtn.layer.removeAllAnimations() footerImg.layer.removeAllAnimations() footerBtn.alpha = 0 footerBtn.setTitle(textToDisplay, forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Regular", size: 18) footerBtn.setTitleColor(UIColor(networking: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 1.0), forState: UIControlState.Normal) //footerBtn.backgroundColor = UIColor(networking: 217/255.0, green: 217/255.0, blue: 217/255.0, alpha: 1.0) UIView.animateKeyframesWithDuration(3.0 /*Total*/, delay:0.0, options: UIViewKeyframeAnimationOptions.CalculationModeLinear, animations: { UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration:0.10, animations:{ footerImg.alpha = 0.01 //Img fades out footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 103/255.0, blue: 00/255.0, alpha: 0.6) //Bg turns to green }) UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration:0.30, animations:{ footerBtn.alpha = 1 //Text and green bg fades in footerBtn.backgroundColor = UIColor(networking: 46/255.0, green: 173/255.0, blue: 11/255.0, alpha: 0.6) //BG turns greener }) UIView.addKeyframeWithRelativeStartTime(0.0, relativeDuration:0.50, animations:{ footerBtn.alpha = 0.01 //Text fades out & bg fade out }) }, completion: { finished in footerImg.alpha = 1 footerBtn.alpha = 1 footerBtn.backgroundColor = UIColor.clearColor() footerBtn.setTitleColor(UIColor(networking: 55/255.0, green: 55/255.0, blue: 55/255.0, alpha: 1.0), forState: UIControlState.Normal) footerBtn.titleLabel!.font = UIFont(name: "HelveticaNeue-Light", size: 18) footerBtn.setTitle("", forState: UIControlState.Normal) //Completion blocks sets values back to norm } ) }//End of 'Finished' animation 

Consulte también este enlace aunque es obj c, muy informativo. http://www.raizlabs.com/dev/2015/01/uiview-animation-sequencing-and-grouping-techniques/