Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIView shake animation

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

}
like image 854
Jack Greenhill Avatar asked Oct 02 '10 05:10

Jack Greenhill


4 Answers

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)
    }
}
like image 28
Ortwin Gentz Avatar answered Nov 15 '22 12:11

Ortwin Gentz


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")
like image 57
Matt Long Avatar answered Nov 15 '22 12:11

Matt Long


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.

like image 21
bandejapaisa Avatar answered Nov 15 '22 12:11

bandejapaisa


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.

like image 39
Julio Bailon Avatar answered Nov 15 '22 13:11

Julio Bailon