Estoy tratando de hacer una sacudida de UIView cuando se presiona un botón.
Estoy adaptando el código que encontré en http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/ .
Sin embargo, al intentar adaptar el siguiente código para agitar una UIView, no funciona:
- (void)animate {
const int numberOfShakes = 8;
const float durationOfShake = 0.5f;
const float vigourOfShake = 0.1f;
CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animation];
CGRect frame = lockView.frame;
CGMutablePathRef shakePath = CGPathCreateMutable();
CGPathMoveToPoint(shakePath, NULL, CGRectGetMinX(frame), CGRectGetMinY(frame));
for (int index = 0; index < numberOfShakes; ++index) {
CGPathAddLineToPoint(shakePath, NULL, CGRectGetMinX(frame) - frame.size.width * vigourOfShake, CGRectGetMinY(frame));
CGPathAddLineToPoint(shakePath, NULL, CGRectGetMinX(frame) + frame.size.width * vigourOfShake, CGRectGetMinY(frame));
}
CGPathCloseSubpath(shakePath);
shakeAnimation.path = shakePath;
shakeAnimation.duration = durationOfShake;
[lockView.layer addAnimation:shakeAnimation forKey:@"frameOrigin"];
}
ios
iphone
animation
cocoa-touch
core-animation
Jack Greenhill
fuente
fuente
Prefiero esta solución que tiene un comportamiento elástico agradable, ideal para una animación de sacudida de contraseña incorrecta.
view.transform = CGAffineTransformMakeTranslation(20, 0); [UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.2 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ view.transform = CGAffineTransformIdentity; } completion:nil];
Swift 3
extension UIView { func shake() { self.transform = CGAffineTransform(translationX: 20, y: 0) UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.2, initialSpringVelocity: 1, options: .curveEaseInOut, animations: { self.transform = CGAffineTransform.identity }, completion: nil) } }
fuente
Aquí está mi versión agradable y simple. Esto simula la sacudida que se obtiene en Mac OS X cuando ingresa incorrectamente. Puede agregar esto como una categoría en UIView si lo desea.
@implementation UIView (DUExtensions) - (void) shake { CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"]; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; animation.duration = 0.6; animation.values = @[ @(-20), @(20), @(-20), @(20), @(-10), @(10), @(-5), @(5), @(0) ]; [self.layer addAnimation:animation forKey:@"shake"]; } @end
Los valores de la animación son el desplazamiento x de la posición actual de la vista. Los valores positivos desplazan la vista hacia la derecha y los valores negativos hacia la izquierda. Al bajarlos sucesivamente, obtienes una sacudida que naturalmente pierde impulso. Puede modificar estos números si lo desea.
fuente
Aquí está la versión rápida como una extensión en caso de que alguien la necesite
extension UIImageView{ func vibrate(){ let animation = CABasicAnimation(keyPath: "position") animation.duration = 0.05 animation.repeatCount = 5 animation.autoreverses = true animation.fromValue = NSValue(CGPoint: CGPointMake(self.center.x - 2.0, self.center.y)) animation.toValue = NSValue(CGPoint: CGPointMake(self.center.x + 2.0, self.center.y)) self.layer.addAnimation(animation, forKey: "position") } }
Esto animará un pequeño UIImageView (alrededor de 15x15). Si necesita animar algo más grande, es posible que desee cambiar el factor de movimiento 2.0 a algo más grande.
fuente
Basado en la respuesta de @bandejapaisa, extensión UIView para Swift 3
extension UIView { func shake() { let animation = CAKeyframeAnimation(keyPath: "transform.translation.x") animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) animation.duration = 0.6 animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0] layer.addAnimation(animation, forKey: "shake") } }
fuente
Puedes probar este fragmento de código:
para llamar al siguiente código, use:
[self earthquake:myObject];
#pragma mark EarthQuake Methods - (void)earthquake:(UIView*)itemView { AudioServicesPlaySystemSound(kSystemSoundID_Vibrate); CGFloat t = 2.0; CGAffineTransform leftQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, t, -t); CGAffineTransform rightQuake = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, t); itemView.transform = leftQuake; // starting point [UIView beginAnimations:@"earthquake" context:itemView]; [UIView setAnimationRepeatAutoreverses:YES]; // important [UIView setAnimationRepeatCount:3]; [UIView setAnimationDuration:0.05]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(earthquakeEnded:finished:context:)]; itemView.transform = rightQuake; // end here & auto-reverse [UIView commitAnimations]; } - (void)earthquakeEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context { if ([finished boolValue]) { UIView* item = (UIView *)context; item.transform = CGAffineTransformIdentity; } }
fuente
Puede llamar a este método en el evento de clic de UIButton
-(void)shakescreen { //Shake screen CGFloat t = 5.0; CGAffineTransform translateRight = CGAffineTransformTranslate(CGAffineTransformIdentity, t, t); CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, -t); self.view.transform = translateLeft; [UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^ { [UIView setAnimationRepeatCount:2.0]; self.view.transform = translateRight; } completion:^(BOOL finished) { if (finished) { [UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^ { self.view.transform = CGAffineTransformIdentity; } completion:NULL]; } }]; }
Espero que esto te ayudará :-)
fuente
Versión de C # Xamarin.iOS de respuesta cómo crear la animación sacudida UIView en IOS está por debajo
CAKeyFrameAnimation keyframeAnimation = CAKeyFrameAnimation.GetFromKeyPath(new NSString("transform.translation.x")); keyframeAnimation.TimingFunction = CAMediaTimingFunction.FromName(CAMediaTimingFunction.EaseInEaseOut); keyframeAnimation.Duration = 0.6f; keyframeAnimation.Values = new NSObject[]{ new NSNumber(-20f), new NSNumber(20f), new NSNumber(-20f), new NSNumber(20f), new NSNumber(-10f), new NSNumber(10f), new NSNumber(-5f), new NSNumber(5f), new NSNumber(0f) }; shakyView.Layer.AddAnimation(keyframeAnimation, "shake");
fuente
Aquí hay uno que usa una función de amortiguador para descomponer el batido:
- (void)shake { CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; animation.duration = 0.5; animation.delegate = self; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = YES; animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]; NSMutableArray* values = [[NSMutableArray alloc] init]; int steps = 100; double position = 0; float e = 2.71; for (int t = 0; t < steps; t++) { position = 10 * pow(e, -0.022 * t) * sin(0.12 * t); NSValue* value = [NSValue valueWithCGPoint:CGPointMake([self center].x - position, [self center].y)]; DDLogInfo(@"Value: %@", value); [values addObject:value]; } animation.values = values; [[self layer] addAnimation:animation forKey:@"position"]; }
fuente
Refactoricé el código de @Matt Long e hice una categoría para
UIView
. Ahora es mucho más reutilizable y fácil de usar.@implementation UIView (Animation) - (void)shakeViewWithOffest:(CGFloat)offset { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"]; [animation setDuration:0.05]; [animation setRepeatCount:6]; [animation setAutoreverses:YES]; [animation setFromValue:@([self center].x-offset)]; [animation setToValue:@([self center].x+offset)]; [self.layer addAnimation:animation forKey:@"position.x"]; } - (void)shake { [self shakeViewWithOffest:7.0f]; } @end
fuente
Implementación de Swift 3 basada en la respuesta de @ Mihael-Isaev
private enum Axis: StringLiteralType { case x = "x" case y = "y" } extension UIView { private func shake(on axis: Axis) { let animation = CAKeyframeAnimation(keyPath: "transform.translation.\(axis.rawValue)") animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear) animation.duration = 0.6 animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0] layer.add(animation, forKey: "shake") } func shakeOnXAxis() { self.shake(on: .x) } func shakeOnYAxis() { self.shake(on: .y) } }
fuente
extension UIView { func shake() { let animation = CAKeyframeAnimation(keyPath: "transform.translation.x") animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) animation.duration = 0.6 animation.values = [-20, 20, -20, 20, -10, 10, -5, 5, 0] self.layer.add(animation, forKey: "shake") }}
fuente
Puedes probar el siguiente código:
+ (void)vibrateView:(UIView*)view { CABasicAnimation *shiverAnimationR; shiverAnimationR = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; shiverAnimationR.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(1)]; //shiverAnimationR.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(-10)]; shiverAnimationR.duration = 0.1; shiverAnimationR.repeatCount = 1000000.0; // Use A high Value shiverAnimationR.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; [view.layer addAnimation: shiverAnimationR forKey:@"shiverAnimationR"]; CABasicAnimation * shiverAnimationL; shiverAnimationL = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; //shiverAnimationL 2.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(10)]; shiverAnimationL.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(-1)]; shiverAnimationL.duration = 0.1; shiverAnimationL.repeatCount = 1000000.0; shiverAnimationL.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; [view.layer addAnimation: shiverAnimationL forKey:@"shiverAnimationL"]; }
Desde el enlace.
fuente
Aquí hay una versión que usa,
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Introducido en iOS 7.
const CGFloat xDelta = 16.0f; [UIView animateKeyframesWithDuration:0.50f delay:0.0f options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{ [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:(1.0/6.0) animations:^{ self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(xDelta, 0.0); }]; [UIView addKeyframeWithRelativeStartTime:(1.0/6.0) relativeDuration:(1.0/6.0) animations:^{ self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(-xDelta, 0.0); }]; [UIView addKeyframeWithRelativeStartTime:(1.0/3.0) relativeDuration:(1.0/3.0) animations:^{ self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformMakeTranslation(xDelta/2.0, 0.0); }]; [UIView addKeyframeWithRelativeStartTime:(2.0/3.0) relativeDuration:(1.0/3.0) animations:^{ self.passwordTextField.transform = self.usernameTextField.transform = CGAffineTransformIdentity; }]; } completion:NULL];
fuente
Aquí hay una extensión de UIView que proporciona una animación de agitación increíble: https://gist.github.com/mourad-brahim/cf0bfe9bec5f33a6ea66
Se proporciona una actualización de Swift5 en los comentarios.
fuente
Swift 4.0:
Basado en la respuesta principal, pero un refinamiento sobre la animación: esto no tiene los saltos al principio y al final de la animación.
let midX = center.x let midY = center.y let rightAnim = CABasicAnimation(keyPath: #keyPath(CALayer.position)) rightAnim.duration = 0.07 rightAnim.autoreverses = true rightAnim.fromValue = CGPoint(x: midX, y: midY) rightAnim.toValue = CGPoint(x: midX + 9, y: midY) let leftAnim = CABasicAnimation(keyPath: #keyPath(CALayer.position)) leftAnim.duration = 0.07 leftAnim.autoreverses = true leftAnim.fromValue = CGPoint(x: midX, y: midY) leftAnim.toValue = CGPoint(x: midX - 9, y: midY) let group = CAAnimationGroup() group.duration = leftAnim.duration + rightAnim.duration group.animations = [rightAnim, leftAnim] group.repeatCount = 3 layer.add(group, forKey: #keyPath(CALayer.position))
fuente