Alex Rivera | Logout

Shake visual effect on iPhone (NOT shaking the device)

Asked 2009-10-27T17:22:46.550
45

On login failure, I'd prefer to avoid showing an alert, it's too fleeting. Showing the alert and then showing the text somewhere on the login screen seems like duplication.

So I'd like for it to graphically shake my login view when the user enters the wrong user ID and password like the Mac login screen does.

Anyone know if there's a way to pull this off, or have any suggestions for another effect I could use?

Edit
Report

4 Answers

98

I think this is a more efficient solution:

Swift:

let anim = CAKeyframeAnimation( keyPath:"transform" )
anim.values = [
    NSValue( CATransform3D:CATransform3DMakeTranslation(-5, 0, 0 ) ),
    NSValue( CATransform3D:CATransform3DMakeTranslation( 5, 0, 0 ) )
]
anim.autoreverses = true
anim.repeatCount = 2
anim.duration = 7/100

viewToShake.layer.addAnimation( anim, forKey:nil )

Obj-C:

CAKeyframeAnimation * anim = [ CAKeyframeAnimation animationWithKeyPath:@"transform" ] ;
anim.values = @[ 
    [ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(-5.0f, 0.0f, 0.0f) ], 
    [ NSValue valueWithCATransform3D:CATransform3DMakeTranslation( 5.0f, 0.0f, 0.0f) ] 
] ;
anim.autoreverses = YES ;
anim.repeatCount = 2.0f ;
anim.duration = 0.07f ;

[ viewToShake.layer addAnimation:anim forKey:nil ] ;

Only one animation object is created and it's all performed at the CoreAnimation level.

answered 2012-02-21T02:11:24.647
58

Using iOS 4+ block based UIKit animations (and loosely based on on jayccrown's answer):

- (void)shakeView:(UIView *)viewToShake
{
    CGFloat t = 2.0;
    CGAffineTransform translateRight  = CGAffineTransformTranslate(CGAffineTransformIdentity, t, 0.0);
    CGAffineTransform translateLeft = CGAffineTransformTranslate(CGAffineTransformIdentity, -t, 0.0);

    viewToShake.transform = translateLeft;

    [UIView animateWithDuration:0.07 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{
        [UIView setAnimationRepeatCount:2.0];
        viewToShake.transform = translateRight;
    } completion:^(BOOL finished) {
        if (finished) {
            [UIView animateWithDuration:0.05 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
                viewToShake.transform = CGAffineTransformIdentity;
            } completion:NULL];
        }
    }];
}
answered 2011-05-11T07:40:04.717
40

I had seen some wobble animation and changed it to shake a view t pixels upright and downleft:

- (void)earthquake:(UIView*)itemView
{
    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:5];
    [UIView setAnimationDuration:0.07];
    [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;
    }
}
answered 2009-12-01T16:35:58.110
4

This UIView category snippet worked for me. It's using 3 CABasingAnimations applied to view's layer.

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>

#define Y_OFFSET 2.0f
#define X_OFFSET 2.0f
#define ANGLE_OFFSET (M_PI_4*0.1f)

@interface UIView (shakeAnimation)

-(BOOL)isShakeAnimationRunning;
-(void)startShakeAnimation;
-(void)stopShakeAnimation;

@end



@implementation UIView (shakeAnimation)

-(BOOL)isShakeAnimationRunning{
     return [self.layer animationForKey:@"shake_rotation"] != nil;
}

-(void)startShakeAnimation{
    CFTimeInterval offset=(double)arc4random()/(double)RAND_MAX;
    self.transform=CGAffineTransformRotate(self.transform, -ANGLE_OFFSET*0.5);
    self.transform=CGAffineTransformTranslate(self.transform, -X_OFFSET*0.5f, -Y_OFFSET*0.5f);

    CABasicAnimation *tAnim=[CABasicAnimation animationWithKeyPath:@"position.x"];
    tAnim.repeatCount=HUGE_VALF;
    tAnim.byValue=[NSNumber numberWithFloat:X_OFFSET];
    tAnim.duration=0.07f;
    tAnim.autoreverses=YES;
    tAnim.timeOffset=offset;
    [self.layer addAnimation:tAnim forKey:@"shake_translation_x"];

    CABasicAnimation *tyAnim=[CABasicAnimation animationWithKeyPath:@"position.y"];
    tyAnim.repeatCount=HUGE_VALF;
    tyAnim.byValue=[NSNumber numberWithFloat:Y_OFFSET];
    tyAnim.duration=0.06f;
    tyAnim.autoreverses=YES;
    tyAnim.timeOffset=offset;
    [self.layer addAnimation:tyAnim forKey:@"shake_translation_y"];

    CABasicAnimation *rAnim=[CABasicAnimation animationWithKeyPath:@"transform.rotation"];
    rAnim.repeatCount=HUGE_VALF;
    rAnim.byValue=[NSNumber numberWithFloat:ANGLE_OFFSET];
    rAnim.duration=0.15f;
    rAnim.autoreverses=YES;
    rAnim.timeOffset=offset;
    [self.layer addAnimation:rAnim forKey:@"shake_rotation"];
}
-(void)stopShakeAnimation{
    [self.layer removeAnimationForKey:@"shake_translation_x"];
    [self.layer removeAnimationForKey:@"shake_translation_y"];
    [self.layer removeAnimationForKey:@"shake_rotati
answered 2012-04-04T08:54:53.817

Your Answer