Shake the visual effect on iPhone (NOT shaking the device)

If the login fails, I would rather not show a warning, it's too fast. Displaying a warning and then displaying the text somewhere on the login screen is like duplication.

Therefore, I would like it to shake my login graphically when the user enters the wrong user ID and password, as the Mac login screen does.

Does anyone know if there is a way to remove this or any suggestions for another effect that I could use?

+44
iphone core-animation
Oct. 27 '09 at 17:22
source share
11 answers

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 all this is done at the CoreAnimation level.

+98
Feb 21 '12 at 2:11
source share

Using iOS 4+ UIKit block animations (and loosely based on jayccrown 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]; } }]; } 
+59
May 11 '11 at 7:40 a.m.
source share

I saw a wobble animation and changed it to shake the view of t pixels vertically and down:

 - (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; } } 
+40
Dec 01 '09 at 16:35
source share

Here is a tutorial that details how to do this in Cocoa. It should be the same for the iPhone (or at least very similar).

http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/

+8
Oct. 27 '09 at 17:44
source share

Simply changing the X-coordinate of the center property of your view can do the trick. If you haven't done any basic animation before, you can do it fairly straightforwardly.

First start the animation to the right, then listen to it to finish, and then go back to the left and so on. Getting the time down so that he “feels right” may take some time.

 - (void)animationFinishCallback:(NSString *)animationID finished:(BOOL)finished context:(void *)context { if ([animationID isEqualToString:@"MoveRight"]) { [UIView beginAnimations:@"MoveLeft" context:NULL]; [UIView setAnimationDuration:1.0]; [UIView setAnimationDelay: UIViewAnimationCurveEaseIn]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(animationFinishCallback:finished:context:)]; myView.center = CGRectMake(newX, newY); [UIView commitAnimations]; } } 
+6
Oct 27 '09 at 17:42
source share

This fragment of the UIView category worked for me. It uses 3 CABasingAnimations used to view the 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_rotation"]; [UIView animateWithDuration:0.2f animations:^{ self.transform=CGAffineTransformRotate(self.transform, ANGLE_OFFSET*0.5); self.transform=CGAffineTransformTranslate(self.transform, X_OFFSET*0.5, Y_OFFSET*0.5f); }]; } @end 

Hope this helps someone :)

+4
Apr 04 2018-12-12T00:
source share

In iOS 7.0 or later, UIKit keyframe animation is available.

 [UIView animateKeyframesWithDuration:0.5 delay:0.0 options:0 animations:^{ [UIView setAnimationCurve:UIViewAnimationCurveLinear]; NSInteger repeatCount = 8; NSTimeInterval duration = 1.0 / (NSTimeInterval)repeatCount; for (NSInteger i = 0; i < repeatCount; i++) { [UIView addKeyframeWithRelativeStartTime:i * duration relativeDuration:duration animations:^{ CGFloat dx = 5.0; if (i == repeatCount - 1) { viewToShake.transform = CGAffineTransformIdentity; } else if (i % 2) { viewToShake.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, -dx, 0.0); } else { viewToShake.transform = CGAffineTransformTranslate(CGAffineTransformIdentity, +dx, 0.0); } }]; } } completion:completion]; 
+2
Sep 08 '14 at 8:53
source share
+1
Jan 23 2018-11-11T00:
source share

I know that the question has already been answered, but since I already implemented something similar earlier, I feel that this will not hurt to add it:

 CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"]; NSArray *transformValues = [NSArray arrayWithObjects: [NSNumber numberWithFloat:((M_PI)/64)], [NSNumber numberWithFloat:(-((M_PI)/64))], [NSNumber numberWithFloat:((M_PI)/64)], [NSNumber numberWithFloat:(-((M_PI)/64))], [NSNumber numberWithFloat:((M_PI)/64)], [NSNumber numberWithFloat:(-((M_PI)/64))], [NSNumber numberWithFloat:0], nil]; [shakeAnimation setValues:transformValues]; NSArray *times = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0.14f], [NSNumber numberWithFloat:0.28f], [NSNumber numberWithFloat:0.42f], [NSNumber numberWithFloat:0.57f], [NSNumber numberWithFloat:0.71f], [NSNumber numberWithFloat:0.85f], [NSNumber numberWithFloat:1.0f], nil]; [shakeAnimation setKeyTimes:times]; shakeAnimation.fillMode = kCAFillModeForwards; shakeAnimation.removedOnCompletion = NO; shakeAnimation.duration = 0.6f; [self.viewToShake.layer addAnimation:shakeAnimation forKey:@"anim"]; 

In addition, since you want the shaking to indicate that the user was unable to log in, you can also think of adding this animation, which shades the red screen while the screen is shaking:

 //Put this in the header (.h) @property (nonatomic, strong) UIView *redView; //Put this in the implementation (.m) @synthesize redView; //Put this in viewDidLoad self.redView = [[UIView alloc] initWithFrame:self.view.frame]; self.redView.layer.opacity = 0.0f; self.redView.layer.backgroundColor = [[UIColor redColor] CGColor]; //Put this wherever you check if the login failed CAKeyframeAnimation *redTint = [CAKeyframeAnimation animationWithKeyPath:@"opacity"]; NSArray *transformValues = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0.2f], [NSNumber numberWithFloat:0.0f], nil]; [redTint setValues:transformValues]; NSArray *times = [NSArray arrayWithObjects: [NSNumber numberWithFloat:0.5f], [NSNumber numberWithFloat:1.0f], nil]; [redTint setKeyTimes:times]; redTint.fillMode = kCAFillModeForwards; redTint.removedOnCompletion = NO; redTint.duration = 0.6f; [self.redView.layer addAnimation:shakeAnimation forKey:@"anim"]; 

Hope this helps!

+1
Jun 20 2018-12-12T00:
source share

Using an automatic layout, I adapted Chris Miles' answer, but animated NSLayoutConstraints as follows:

 NSLayoutConstraint *left = ... NSLayoutConstraint *right = ... [UIView animateWithDuration:0.08 delay:0.0 options:UIViewAnimationOptionAutoreverse|UIViewAnimationOptionRepeat animations:^{ [UIView setAnimationRepeatCount:3]; left.constant = 15.0; right.constant = 25.0; [self.view layoutIfNeeded]; } completion:^(BOOL finished) { if (finished) { [UIView animateWithDuration:0.08 animations:^{ left.constant = 20.0; right.constant = 20.0; [self.view layoutIfNeeded]; } completion:NULL]; } }]; 
0
Sep 25 '13 at 20:58
source share

The solution I used for the limitations that I set in my storyboard. Without using animateWithDuration.

 @IBOutlet var balloonHorizontalConstraint: NSLayoutConstraint! NSTimer.scheduledTimerWithTimeInterval(0.04, target: self, selector: "animateBalloon", userInfo: nil, repeats: true) func animateBalloon() { switch balloonHorizontalConstraint.constant { case -46: balloonHorizontalConstraint.constant = -50 default: balloonHorizontalConstraint.constant = -46 } } 

In my case, the animation just continued, but I pop my control controller after a few seconds, this also stops my timer.

0
Mar 09 '16 at 20:46 on
source share



All Articles