2010-01-04 15 views
10

Quiero crear una animación con varios fotogramas clave. Quiero mi Capa (un botón en este caso) para escalar hasta 1.5 y luego hasta 0.5 y luego hasta 1.2 y luego hasta 0.8 y 1.0.¿Cómo funciona CAKeyframeAnimation Scale?

También quiero EaseIn y EaseOut de cada fotograma clave.

Como se puede imaginar, esto creará un efecto Springy/Bounce en el acto.

En otras partes de mi aplicación he estado usando CAKeyframeAnimation de esta manera (vea el código a continuación). Esto crea una animación elástica similar, pero para la posición xey.

¿Puedo adaptar el siguiente código para afectar la escala en lugar de la posición?

¡Gracias de antemano!

- (CAAnimation*)monInAnimation { 
CGMutablePathRef path = CGPathCreateMutable(); 
CGPathMoveToPoint(path,NULL,113,320); 
CGPathAddLineToPoint(path, NULL, 113.5, 283); 
CGPathAddLineToPoint(path, NULL, 113.5, 179); 
CGPathAddLineToPoint(path, NULL, 113.5, 207); 
CGPathAddLineToPoint(path, NULL, 113.5, 187); 
CGPathAddLineToPoint(path, NULL, 113.5, 199); 
CGPathAddLineToPoint(path, NULL, 113.5, 193); 
CGPathAddLineToPoint(path, NULL, 113.5, 195); 
CGPathAddLineToPoint(path, NULL, 113.5, 194); 

CAKeyframeAnimation * 
animation = [CAKeyframeAnimation 
      animationWithKeyPath:@"position"]; 

[animation setPath:path]; 
[animation setDuration:1.5]; 
[animation setCalculationMode:kCAAnimationLinear]; 
NSArray *arr = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], 
[NSNumber numberWithFloat:0.12], 
[NSNumber numberWithFloat:0.24], 
[NSNumber numberWithFloat:0.36], 
[NSNumber numberWithFloat:0.48], 
[NSNumber numberWithFloat:0.60], 
[NSNumber numberWithFloat:0.72], 
[NSNumber numberWithFloat:0.84], 
[NSNumber numberWithFloat:1.0],nil]; 

[animation setKeyTimes:arr]; 
[animation setTimingFunctions:[NSArray arrayWithObjects:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn], 
           [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], 
           [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
           [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
           [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
           [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
           [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
           [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
           [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], nil]]; 
//[animation setAutoreverses:YES]; 
CFRelease(path); 
return animation; 

}

- (void)monBtnIn { 

[monButton.layer setPosition:CGPointMake(113.5,194)]; 
[monButton.layer addAnimation:[self monInAnimation] 
        forKey:@"position"]; 

}

Respuesta

27

dos soluciones alternativas:

En primer lugar, también puede animar la propiedad transform.

Usando el código de Brad, pero utilizando @ "transform" para la ruta de teclas. La ventaja principal es que usted no tiene que calcular el marco real, sino que proporcionan un simple factor de escala:

Objective-C:

CAKeyframeAnimation *boundsOvershootAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"]; 

CATransform3D startingScale = CATransform3DScale (layer.transform, 0, 0, 0); 
CATransform3D overshootScale = CATransform3DScale (layer.transform, 1.2, 1.2, 1.0); 
CATransform3D undershootScale = CATransform3DScale (layer.transform, 0.9, 0.9, 1.0); 
CATransform3D endingScale = layer.transform; 

NSArray *boundsValues = [NSArray arrayWithObjects:[NSValue valueWithCATransform3D:startingScale], 
                [NSValue valueWithCATransform3D:overshootScale], 
                [NSValue valueWithCATransform3D:undershootScale], 
                [NSValue valueWithCATransform3D:endingScale], nil]; 
[boundsOvershootAnimation setValues:boundsValues]; 

NSArray *times = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0f], 
        [NSNumber numberWithFloat:0.5f], 
        [NSNumber numberWithFloat:0.9f], 
        [NSNumber numberWithFloat:1.0f], nil];  
[boundsOvershootAnimation setKeyTimes:times]; 


NSArray *timingFunctions = [NSArray arrayWithObjects:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], 
          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
          nil]; 
[boundsOvershootAnimation setTimingFunctions:timingFunctions]; 
boundsOvershootAnimation.fillMode = kCAFillModeForwards; 
boundsOvershootAnimation.removedOnCompletion = NO; 

Swift 4:

let boundsOvershootAnimation = CAKeyframeAnimation(keyPath: "transform") 

let startingScale = CATransform3DScale(layer.transform, 0, 0, 0) 
let overshootScale = CATransform3DScale(layer.transform, 1.2, 1.2, 1.0) 
let undershootScale = CATransform3DScale(layer.transform, 0.9, 0.9, 1.0) 
let endingScale = layer.transform 

boundsOvershootAnimation.values = [startingScale, overshootScale, undershootScale, endingScale] 

boundsOvershootAnimation.keyTimes = [0.0, 0.5, 0.9, 1.0].map { NSNumber(value: $0) } 

boundsOvershootAnimation.timingFunctions = [ 
    CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseOut), 
    CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut), 
    CAMediaTimingFunction(name:kCAMediaTimingFunctionEaseInEaseOut) 
] 

boundsOvershootAnimation.fillMode = kCAFillModeForwards 
boundsOvershootAnimation.isRemovedOnCompletion = false 

En segundo lugar, y probablemente más fácil, está utilizando FTUtils, un contenedor de código abierto para la animación principal. Incluye una animación stock "elástica".

Se puede conseguir en: http://github.com/neror/ftutils

+1

Esto funciona genial ... Gracias. Yo agregué una .duración tú. Revisaré FTUtilis - Gracias. – Jonathan

+2

Podrías simplemente usar números usando Keypath como "transform.scale". ¿No es así? –

4

No sé si se puede utilizar un CAKeyframeAnimation para la animación de la escala de un UIView, pero puede hacerlo con un CABasicAnimation y ajuste de la desde las propiedades Valor y Valor, y usando eso para animar la propiedad de transformación:

- (CAAnimation*)monInAnimation 
{ 
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"]; 
    animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.0, 1.0, 1.0)]; 
    animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(3.0, 3.0, 3.0)]; 
    [animation setDuration:1.5]; 
    [animation setAutoreverses:YES]; 
    return animation; 
} 

- (IBAction)monBtnIn 
{ 
    [monButton.layer addAnimation:[self monInAnimation] forKey:@"transform"]; 
} 
+1

Gracias akosma! He intentado de esa manera escalar, pero no sé cómo agregar más de 2 fotogramas clave. Con 'setAutoreverses' va de A a B y luego de vuelta a A. Quiero ir de A a B a C a D. ¿sabes si puedes agregar más 'desde Valores' y 'Valores a'? Gracias – Jonathan

+0

Eche un vistazo a la clase CAAnimationGroup. Hereda de CAAnimation, y con él puede "encadenar" CABasicAnimations individuales, cada una con su propio valor y valor. ¿Podría ser esto lo que estás buscando? –

+0

Scratch that, he comprobado los documentos y parece que las animaciones en un CAAnimationGroup se ejecutan simultáneamente, no una después de la otra. Gorrón. Debe establecer delegados para sus animaciones, y luego comenzar una nueva animación cuando la anterior haya terminado. –

8

R A menos que establezca el camino de su CAKeyframeAnimation, querrá configurar los fotogramas clave. He creado un efecto de "pop-in" antes animando el tamaño de los límites de una capa:

CAKeyframeAnimation *boundsOvershootAnimation = [CAKeyframeAnimation animationWithKeyPath:@"bounds.size"]; 
CGSize startingSize = CGSizeZero; 
CGSize overshootSize = CGSizeMake(targetSize.width * (1.0f + POPINOVERSHOOTPERCENTAGE), targetSize.height * (1.0f + POPINOVERSHOOTPERCENTAGE)); 
CGSize undershootSize = CGSizeMake(targetSize.width * (1.0f - POPINOVERSHOOTPERCENTAGE), targetSize.height * (1.0f - POPINOVERSHOOTPERCENTAGE)); 
NSArray *boundsValues = [NSArray arrayWithObjects:[NSValue valueWithCGSize:startingSize], 
         [NSValue valueWithCGSize:overshootSize], 
         [NSValue valueWithCGSize:undershootSize], 
         [NSValue valueWithCGSize:targetSize], nil]; 
[boundsOvershootAnimation setValues:boundsValues]; 

NSArray *times = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0f], 
        [NSNumber numberWithFloat:0.5f], 
        [NSNumber numberWithFloat:0.9f], 
        [NSNumber numberWithFloat:1.0f], nil];  
[boundsOvershootAnimation setKeyTimes:times]; 


NSArray *timingFunctions = [NSArray arrayWithObjects:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], 
          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
          [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut], 
          nil]; 
[boundsOvershootAnimation setTimingFunctions:timingFunctions]; 
boundsOvershootAnimation.fillMode = kCAFillModeForwards; 
boundsOvershootAnimation.removedOnCompletion = NO; 

donde POPINOVERSHOOTPERCENTAGE es la fracción por el cual quería sobrepasar el tamaño de destino de la capa.

+0

Gracias Brad. Este es exactamente el tipo de control que estaba buscando. – Jonathan

12

transform.scale, alguien?

let anim = CAKeyframeAnimation(keyPath: "transform.scale") 
anim.values = [0, 1, 0, 1, 0, 1, 0] 
anim.duration = 4.0 

smallView.layer.addAnimation(anim, forKey: "what") 

totalmente sin relación, si va a utilizar flota en la matriz de valores, debe añadirlos como NSNumbers, de lo contrario se acababan de terminar como 0s!

anim.values = [0.0, 1.2, 0.9, 1.0].map { NSNumber(double: $0) } 
+0

Estaba usando "transformar", pero eso causaba problemas con 'rotationMode = kCAAnimationRotateAuto'. El uso de "transform.scale" hizo que la declaración de valores también sea más simple. Muchas gracias. – Henrik

Cuestiones relacionadas