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
I know the question is already answered, but since I have already implemented something like this previously, I feel it can't 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"];
Also, since you want the shaking to indicate that the user failed to log in, you might also consider adding this animation that tints the screen red while the screen shakes:
//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!
Here's a tutorial that details how to do it in Cocoa. Should be the same for the iPhone (or at least quite similar).
http://www.cimgf.com/2008/02/27/core-animation-tutorial-window-shake-effect/
very easy shake categorie for UIVoew
https://github.com/jonasschnelli/UIView-I7ShakeAnimation
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_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 it helpes someone :)
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;
}
}