Cómo CAAnimation me ayudó a vencer mi miedo a crear animaciones

Este artículo se centra en el uso de animaciones de CA en iOS para crear animaciones fluidas.

Durante mis primeros días trabajando con iOS, me ponía muy nervioso cada vez que un diseñador se me acercaba y me pedía algo de animación en la aplicación en la que estaban trabajando.

Solía ​​pensar que era fácil idear el diseño de las animaciones, pero implementarlo, por otro lado, era una tarea muy difícil.

Obtendría ayuda de Google, StackOverflow y mis compañeros para la implementación.

Durante el proceso desarrollé una fobia a las animaciones y siempre traté de evitarlas. Pero todo eso cambió un día.

Encontrar CAAnimación

Una vez, tuve que animar una secuencia de imágenes en una vista. Entonces, ¿cuál fue mi primer paso? ¡Obviamente, StackOverflow!

El primer enlace obtuvo el código.

let image_1 = UIImage(named: "image-1")! let image_2 = UIImage(named: "image-2")! let image_3 = UIImage(named: "image-3")! let images = [image_1, image_2, image_3] let animatedImage = UIImage.animatedImage(with: images, duration: 2.0) imageView.image = animatedImage

Parece bastante sencillo, ¿verdad? Si fuera así de simple, no estaría escribiendo este artículo.

Esta es la animación que se requería:

Y como probablemente haya quedado claro, no estaba ni cerca de eso. Estaba atorada. ¿Cómo se suponía que iba a hacer tantas personalizaciones en esa animación y sincronizarlas todas?

Entonces mi colega me dijo que probara CAAnimation. Lo leí y lo probé en un proyecto de muestra. Para mi asombro, fue realmente poderoso y fácil de usar.

¿Qué es la animación básica?

Core Animation te ayuda a ejecutar múltiples animaciones con un uso de CPU casi nulo.

Le brinda una alta velocidad de fotogramas y muchas personalizaciones que puede usar con muy poco código.

Puede encontrar más detalles en los documentos aquí: //developer.apple.com/documentation/quartzcore

Pude hacer una implementación básica en unas pocas horas:

func addAnimation(firstImageView: UIImageView, secondImageView: UIImageView) { let basicAnimation1 = getBasicAnimation(withInitialPostion: centerPosition, finalPos: finalPosition) firstImageView.layer.add(basicAnimation1, forKey: "position") let basicAnimation2 = self.getBasicAnimation(withInitialPostion: self.initalPosition, finalPos: self.centerPosition) secondImageView.layer.add(basicAnimation2, forKey: "position") self.addNextImage(forImageView: firstImageView) } func getBasicAnimation(withInitialPostion initialPos: CGPoint, finalPos: CGPoint) -> CABasicAnimation { let basicAnimation = CABasicAnimation(keyPath: "position") basicAnimation.fromValue = NSValue(cgPoint: initialPos) basicAnimation.toValue = NSValue(cgPoint: finalPos) basicAnimation.duration = 1 basicAnimation.isRemovedOnCompletion = false basicAnimation.fillMode = CAMediaTimingFillMode.forwards basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) return basicAnimation }

Para esta implementación, utilicé CABasicAnimation .

La clase CABasicAnimation le ayuda a animar una propiedad de capa (que puede ser color de fondo, opacidad, posición, escala) entre dos valores. Solo tienes que dar un valor inicial y final, y el resto se encargará. La animación comienza inmediatamente en el siguiente ciclo de ejecución, como se describe con más detalle aquí.

Ahora, volvamos a nuestro problema.

Para implementar esto, tomé dos vistas de imagen y les agregué dos imágenes separadas. Luego seguí animándolos uno tras otro usando CAAnimation.

Puedes encontrar el código fuente aquí.

Si examinas el último gif, verás que algo anda mal. Antes de que la primera imagen de una caja de regalo se pierda de vista, los auriculares parpadean brevemente y luego la imagen se mueve hacia arriba.

¿Por qué está pasando esto?

Es porque tan pronto agregamos la animación a la vista de imagen, estamos agregando la siguiente imagen a esa vista (líneas número 5 y 6):

private func addAnimation(firstImageView: UIImageView, secondImageView: UIImageView) { let basicAnimation1 = getBasicAnimation(withInitialPostion: centerPosition, finalPos: finalPosition) firstImageView.layer.add(basicAnimation1, forKey: "position") let basicAnimation2 = self.getBasicAnimation(withInitialPostion: self.initalPosition, finalPos: self.centerPosition) secondImageView.layer.add(basicAnimation2, forKey: "position") self.addNextImage(forImageView: firstImageView) }

Aquí estamos luchando con el problema de cómo sincronizar ambas imágenes en la animación. Pero siempre hay una solución con CAAnimation.

Transacciones de CA

Las transacciones de CA nos ayudan a sincronizar varias animaciones juntas. Se asegura de que todas las animaciones que hemos agrupado comiencen al mismo tiempo.

Además, puede dar un bloque de finalización a sus animaciones, que se ejecutará cuando se completen todas sus animaciones en un paquete.

Puedes leer más sobre esto aquí.

private func addAnimation(firstImageView: UIImageView, secondImageView: UIImageView) { CATransaction.begin() CATransaction.setCompletionBlock { self.addNextImage(forImageView: firstImageView) } let basicAnimation1 = getBasicAnimation(withInitialPostion: centerPosition, finalPos: finalPosition) firstImageView.layer.add(basicAnimation1, forKey: "position") CATransaction.commit() let basicAnimation2 = self.getBasicAnimation(withInitialPostion: self.initalPosition, finalPos: self.centerPosition) secondImageView.layer.add(basicAnimation2, forKey: "position") }

Empiece por escribir CATransaction.begin(). Luego, escriba todas las animaciones que desee sincronizar. Finalmente, llame a CATransaction.commit()que iniciará la animación en el bloque.

Veamos cómo se ve nuestra animación ahora:

Una última cosa que tenía que hacer era agregar el efecto Spring a la animación. Afortunadamente, CAAnimation también tuvo una solución para esto.

Animación de primavera de CA

CA Spring Animation, cuando se agrega a una capa, le da un efecto de resorte, de modo que parece ser atraído hacia un objetivo por un resorte.

Cuanto más lejos esté la capa del objetivo, mayor será la aceleración hacia ella.

Permite controlar los atributos físicos, como la amortiguación y la rigidez del resorte. - Documentos

Puede leer más sobre esto en la documentación de Apple: //developer.apple.com/documentation/quartzcore/caspringanimation

Implementémoslo en nuestro código existente:

private func getSpringAnimation(withInitialPostion initialPos: CGPoint, finalPos: CGPoint) -> CASpringAnimation { let basicAnimation = CASpringAnimation(keyPath: "position") basicAnimation.fromValue = NSValue(cgPoint: initialPos) basicAnimation.toValue = NSValue(cgPoint: finalPos) basicAnimation.duration = basicAnimation.settlingDuration basicAnimation.damping = 14 basicAnimation.initialVelocity = 5 basicAnimation.isRemovedOnCompletion = false basicAnimation.fillMode = CAMediaTimingFillMode.forwards return basicAnimation }

Mi trabajo está hecho aquí.

En resumen, estas son algunas de las ventajas de usar CA Animations:

  • Son fáciles de usar e implementar
  • Hay muchas personalizaciones disponibles.
  • Es posible sincronizar varias animaciones
  • Almost zero CPU usage

These the just a few of the advantages. The possibilities are endless.

Now, whenever requirements come for animation, I feel confident designing and implementing them. And I hope you also feel the same way after reading this.

Feel free to leave any suggestions or feedback.