objective c - Cómo animar sincrónicamente un UIView y un CALayer
objective-c cocoa-touch (2)
Si quieres el fragmento de David Rönnqvist en Swift 3.0 , aquí lo tienes:
CATransaction.begin()
CATransaction.setAnimationDuration(1.0)
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut))
// Layer animation
let myAnimation = CABasicAnimation(keyPath: "frame");
myAnimation.toValue = NSValue(cgRect: myNewFrame)
myAnimation.fromValue = NSValue(cgRect: myLayer.frame)
myLayer.frame = myNewFrame
myLayer.add(myAnimation, forKey: "someKeyForMyAnimation")
// Outer animation
let outerAnimation = CABasicAnimation(keyPath: "frame")
outerAnimation.toValue = NSValue(cgRect: myNewOuterFrame)
outerAnimation.fromValue = NSValue(cgRect: outerView.frame)
outerView.layer.frame = myNewOuterFrame
outerView.layer.add(outerAnimation, forKey: "someKeyForMyOuterAnimation")
CATransaction.commit()
Para ilustrar esta pregunta, escribí un proyecto Xcode muy pequeño en Github (dos clases, descarga de 11kb). Eche un vistazo a la esencia aquí o use git clone [email protected]: 93982af3b65d2151672e.git .
Por favor considere el siguiente escenario. Una vista personalizada mía, llamada ''vista de contenedor'' , contiene dos pequeños cuadrados. Ilustrado en esta captura de pantalla:
El cuadrado azul es una instancia de UIView
22x22 pt y el cuadrado rojo es una instancia de CALayer
22x22 pt. Para los propósitos de esta pregunta, quiero que los dos cuadrados se "peguen" a la esquina inferior derecha de la vista exterior, mientras animo el marco de esa vista exterior.
Cambio el marco de la vista del contenedor dentro del método de clase animateWithDuration:delay:options:animations:completion:
con un parámetro de suavizado no predeterminado de tipo UIViewAnimationCurveEaseOut
.
[UIView animateWithDuration:1.0 delay:0
options:UIViewAnimationCurveEaseOut
animations:^{ _containerView.frame = randomFrame; }
completion:nil];
Nota: tanto el marco azul de UIView como el rojo de CALayer están establecidos en el método setFrame:
de la vista del contenedor, pero los resultados habrían sido los mismos si hubiera establecido la propiedad autoresizingMask de autoresizingMask
azul en UIView márgenes izquierdo y superior flexibles.
En la animación resultante, el cuadrado azul se "pega" a la esquina de la forma que lo pretendía, pero el cuadrado rojo ignora por completo el tiempo y la facilidad de la animación. Supongo que esto se debe a la función de animación implícita de Core Animation, una función que me ha ayudado en muchas ocasiones antes.
Aquí hay algunas capturas de pantalla en la secuencia de animación que ilustran la asincronía de los dos cuadrados:
A la pregunta: ¿hay una manera de sincronizar los dos cambios de cuadro para que tanto el CALayer rojo como el UIView azul se muevan con la misma duración de animación y curva de suavizado, pegados entre sí como si fueran una vista?
PS Por supuesto, el resultado visual requerido de los dos cuadrados pegados podría lograrse de muchas maneras, por ejemplo, haciendo que ambas capas se conviertan en CALayer
o UIView
s, pero el proyecto en el que se encuentra el problema real tiene una causa muy legítima para que uno sea un CALayer y el otro para ser un UIView.
Supongo que tiene la posición final correcta y que, después de la animación, la vista y la capa están alineadas. Eso no tiene nada que ver con la animación, solo con la geometría.
Cuando cambias la propiedad de un CALayer
que no es la capa de una vista (como en tu caso), se animará implícitamente a su nuevo valor. Para personalizar esta animación puede usar una animación explícita, como una animación básica. Cambiar el cuadro en una animación básica se vería así.
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[myAnimation setToValue:[NSValue valueWithCGRect:myNewFrame]];
[myAnimation setFromValue:[NSValue valueWithCGRect:[myLayer frame]]];
[myAnimation setDuration:1.0];
[myAnimation setTimingFunction:[CAMediaTimingFuntion functionWithName: kCAMediaTimingFunctionEaseOut]];
[myLayer setFrame:myNewFrame];
[myLayer addAnimation:myAnimation forKey:@"someKeyForMyAnimation"];
Si la función de tiempo y la duración de ambas animaciones son las mismas, entonces deben permanecer alineadas.
También tenga en cuenta que las animaciones explícitas no cambian el valor y el sombrero que tiene para agregar la animación y establecer el nuevo valor (como en la muestra anterior)
Sí, hay varias formas de lograr el mismo efecto. Un ejemplo es tener la vista y la capa subvistas de la misma subvista (que a su vez es una subvista del marco externo).
Editar
No se puede agrupar fácilmente la animación UIView con una animación explícita. Tampoco puede usar un grupo de animación (ya que los está aplicando a capas diferentes), pero puede usar una transacción CAT:
[CATransaction begin];
[CATransaction setAnimationDuration:1.0];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFuntion functionWithName: kCAMediaTimingFunctionEaseOut]];
// Layer animation
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[myAnimation setToValue:[NSValue valueWithCGRect:myNewFrame]];
[myAnimation setFromValue:[NSValue valueWithCGRect:[myLayer frame]]];
[myLayer setFrame:myNewFrame];
[myLayer addAnimation:myAnimation forKey:@"someKeyForMyAnimation"];
// Outer animation
CABasicAnimation *outerAnimation = [CABasicAnimation animationWithKeyPath:@"frame"];
[outerAnimation setToValue:[NSValue valueWithCGRect:myNewOuterFrame]];
[outerAnimation setFromValue:[NSValue valueWithCGRect:[outerView frame]]];
[[outerView layer] setFrame:myNewOuterFrame];
[[outerView layer] addAnimation:outerAnimation forKey:@"someKeyForMyOuterAnimation"];
[CATransaction commit];