¿Cómo se implementa UIView’s + animateWithDuration: animaciones?

tl; dr Es una pila (técnicamente una lista vinculada) de estados de animación pendientes.

UIView es una envoltura alrededor de CALayer. Nada más y nada menos. En ese espíritu, podemos comenzar a ver cómo iOS configura el “contexto de animación implícito”.

Una vista es, por defecto, el delegado de su capa, lo que significa que todo tipo de métodos útiles para dibujar, diseñar y acciones (básicamente, cambios en las rutas clave relevantes como el marco o la escala) se envían a través de ella. En particular, CoreAnimation expone dos métodos delegados con las firmas:

  - (id ) defaultActionForKey: clave (NSString *)
 - (id ) actionForKey: clave (NSString *)

específicamente para que su delegado pueda intervenir y especificar cómo se deben manejar los cambios de propiedad. Si uno devuelve NSNull o cualquier otra cosa nula, el cambio de propiedad simplemente se confirma en el árbol de capas con una sacudida y eso es todo. Sin embargo, si se devuelve una animación (por ejemplo, una instancia simple de CABasicAnimation), el cambio de propiedad ejecutará la animación para nosotros.

De esto, todo lo que nos falta es cómo almacenar ese estado. Después de todo, los bloques de animación se pueden anidar, ¡y ese bloque interno heredaría la duración y las acciones del bloque externo y arruinaría el efecto si utilizáramos un modelo plano! En cambio, cuando llamas a + animateWithDuration … y parientes, UIKit asigna una estructura para mantener el estado de animación que tiene un miembro que hace referencia al siguiente estado de animación si existe.

En la práctica, lo que esto significa es que puede anidar animaciones simplemente asignando una nueva estructura y luego configurando el campo “próxima animación” de la anterior. Cuando llega el momento de ejecutar, desactiva el estado de la animación, espera a que se completen todas las acciones y luego verifica si existe la siguiente animación. Si es así, ejecute ese y espere hasta la saciedad hasta que llegue al contexto de animación más alto.

Es útil pensar cómo escribiría el nuevo método en términos de la API anterior. Aquí está la API más antigua:

  + (vacío) beginAnimations: (NSString *) animationID context: (void *) context;
 + (nulo) commitAnimations;

Notarás que también son métodos de clase. Las animaciones pueden aplicarse a muchas vistas diferentes dentro de uno de estos contextos de animación, por lo que es apropiado que estos sean métodos de clase.

Se utiliza un objeto privado para mantener todo el estado de animación creado durante el contexto y comunicar esa información a CoreAnimation. Cuando se llama a +commitAnimations , las animaciones se adjuntan a las capas de las vistas y CoreAnimation se hace cargo después del final del ciclo de ejecución.

Por lo tanto, puede pensar en el bloque de animations como un contenedor alrededor de +beginAnimations:context: y [+ commitAnimations] y los otros parámetros de los diversos métodos en la categoría UIViewAnimationWithBlocks de UIView que establecen todas las duraciones, demoras, etc.en ese contexto.