UIView shake animation

IosIphoneAnimationCocoa TouchCore Animation

Ios Problem Overview


i'm trying to make a UIView shake when a button is pressed.

I am adapting the code I found on http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/.

However, by trying to adapt the following code to shake a UIView, it does not work:

- (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 Solutions


Solution 1 - Ios

I wrote that post. It's overkill for a UIView, plus the parameters are geared toward an OSX app. Do this instead.

CABasicAnimation *animation = 
                         [CABasicAnimation animationWithKeyPath:@"position"];
[animation setDuration:0.05];
[animation setRepeatCount:8];
[animation setAutoreverses:YES];
[animation setFromValue:[NSValue valueWithCGPoint:
               CGPointMake([lockView center].x - 20.0f, [lockView center].y)]];
[animation setToValue:[NSValue valueWithCGPoint:
               CGPointMake([lockView center].x + 20.0f, [lockView center].y)]];
[[lockView layer] addAnimation:animation forKey:@"position"];

You'll have to play with the duration and repeatCount parameters as well as the x distance from center in the from and to values, but it should give you what you need. I hope that helps. Let me know if you have any questions.




Swift 3.0

let midX = lockView.center.x
let midY = lockView.center.y

let animation = CABasicAnimation(keyPath: "position")
animation.duration = 0.06
animation.repeatCount = 4
animation.autoreverses = true
animation.fromValue = CGPoint(x: midX - 10, y: midY)
animation.toValue = CGPoint(x: midX + 10, y: midY)
layer.add(animation, forKey: "position")

Solution 2 - Ios

I prefer this solution that has a nice springy behavior, ideal for a wrong-password shake animation.

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)
    }
}

Solution 3 - Ios

Here's my nice and simple looking version This simulates the shake you get on Mac OS X when you do an incorrect login. You could add this as a category on UIView if you like.

@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

The animation values are the x offset from the views current position. Positive values shifting the view to the right, and negative values to the left. By successively lowering them, you get a shake that naturally loses momentum. You can tweak these numbers if you like.

Solution 4 - Ios

Here is the swift version as an extension in case anybody needs it

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")
    }
}

This will animate an small UIImageView (around 15x15). If you need to animate something bigger you may want to change the 2.0 factor of movement to something greater.

Solution 5 - Ios

Based on @bandejapaisa answer, UIView extension for 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")
    }
}

Solution 6 - Ios

You can try this piece of code:

to call the code below, 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;
   }
}

Solution 7 - Ios

You can call this method on UIButton click event

-(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];
      }
  }];
}

Hope this will help you :-)

Solution 8 - Ios

> @imike answer in Swift 4.2

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")
}}

Solution 9 - Ios

C# Xamarin.iOS version of answer how to create UIView shake animation in iOS is below

        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");

Solution 10 - Ios

Here's one that uses a damper function to decay the shake:

- (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"];

}

Solution 11 - Ios

I refactored @Matt Long code and made a category to UIView. Now it's much more reusable and easy to use.

@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

Solution 12 - Ios

Swift 3 implementation based on @Mihael-Isaev answer

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)
    }
}

Solution 13 - Ios

Swift 4.0:

Based on the top answer but a refinement over the animation: This does not have the jumps at the start and end of animation.

    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))

Solution 14 - Ios

You can try the following code:

+ (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"];

}

From the link.

Solution 15 - Ios

Here is a version using,

+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion

Introduced in 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];

Solution 16 - Ios

Here is a UIView extension providing an awesome shake animation: https://gist.github.com/mourad-brahim/cf0bfe9bec5f33a6ea66

A Swift5 update is provided on the comments.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJack GreenhillView Question on Stackoverflow
Solution 1 - IosMatt LongView Answer on Stackoverflow
Solution 2 - IosOrtwin GentzView Answer on Stackoverflow
Solution 3 - IosbandejapaisaView Answer on Stackoverflow
Solution 4 - IosJulio BailonView Answer on Stackoverflow
Solution 5 - IosimikeView Answer on Stackoverflow
Solution 6 - IosWrightsCSView Answer on Stackoverflow
Solution 7 - IosBirjuView Answer on Stackoverflow
Solution 8 - IosiOS LifeeView Answer on Stackoverflow
Solution 9 - IosAlex SorokoletovView Answer on Stackoverflow
Solution 10 - IosJasper BluesView Answer on Stackoverflow
Solution 11 - IosSzuView Answer on Stackoverflow
Solution 12 - IosLory HuzView Answer on Stackoverflow
Solution 13 - IosVladView Answer on Stackoverflow
Solution 14 - IosHDdeveloperView Answer on Stackoverflow
Solution 15 - IosRichard StellingView Answer on Stackoverflow
Solution 16 - IosMourad BrahimView Answer on Stackoverflow