方法1
|
question:
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?
answer:
1.
Simply changing the X coordinate of the center property of your view might do the trick. If you haven't done any core animation before it's pretty straight-forward.
First, start an animation right, then listen for it to finish, and then move back to the left, and so on. Getting the timing down so it "feels right" might take a while.
- (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]; } }
2.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; } }
3.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]; } }]; }
4.QuartzCore
#import "UIView+I7ShakeAnimation.h" #import <QuartzCore/QuartzCore.h> @implementation UIView (I7ShakeAnimation) category模式 - (void)shakeX { [self shakeXWithOffset:40.0 breakFactor:0.85 duration:1.5 maxShakes:30]; } - (void)shakeXWithOffset:(CGFloat)aOffset breakFactor:(CGFloat)aBreakFactor duration:(CGFloat)aDuration maxShakes:(NSInteger)maxShakes { CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; [animation setDuration:aDuration]; NSMutableArray *keys = [NSMutableArray arrayWithCapacity:20]; int infinitySec = maxShakes; while(aOffset > 0.01) { [keys addObject:[NSValue valueWithCGPoint:CGPointMake(self.center.x - aOffset, self.center.y)]]; aOffset *= aBreakFactor; [keys addObject:[NSValue valueWithCGPoint:CGPointMake(self.center.x + aOffset, self.center.y)]]; aOffset *= aBreakFactor; infinitySec--; if(infinitySec <= 0) { break; } } animation.values = keys; [self.layer addAnimation:animation forKey:@"position"]; } @end
#import "AView.h" #import "UIView+I7ShakeAnimation.h" @implementation AView - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self shakeX]; } @end
#import "ExampleViewController.h" #import "AView.h" #import "UIView+I7ShakeAnimation.h" @implementation ExampleViewController - (void)viewDidLoad { [super viewDidLoad]; AView *aView = [[AView alloc] initWithImage:[UIImage imageNamed:@"logo.png"]]; aView.frame = CGRectMake(122,170,75,75); aView.userInteractionEnabled = YES; [self.view addSubview:aView]; [aView shakeX]; [aView release]; }
5.同4,变种
CAKeyframeAnimation * anim = [ CAKeyframeAnimation animationWithKeyPath:@"transform" ] ; anim.values = [ NSArray arrayWithObjects: [ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(-5.0f, 0.0f, 0.0f) ], [ NSValue valueWithCATransform3D:CATransform3DMakeTranslation(5.0f, 0.0f, 0.0f) ], nil ] ; anim.autoreverses = YES ; anim.repeatCount = 2.0f ; anim.duration = 0.07f ; [ viewToShake.layer addAnimation:anim forKey:@"nil" ] ;
6.mac开发
- (CAKeyframeAnimation *)shakeAnimation:(NSRect)frame { CAKeyframeAnimation *shakeAnimation = [CAKeyframeAnimation animation]; CGMutablePathRef shakePath = CGPathCreateMutable(); CGPathMoveToPoint(shakePath, NULL, NSMinX(frame), NSMinY(frame)); int index; for (index = 0; index < numberOfShakes; ++index) { CGPathAddLineToPoint(shakePath, NULL, NSMinX(frame) - frame.size.width * vigourOfShake, NSMinY(frame)); CGPathAddLineToPoint(shakePath, NULL, NSMinX(frame) + frame.size.width * vigourOfShake, NSMinY(frame)); } CGPathCloseSubpath(shakePath); shakeAnimation.path = shakePath; shakeAnimation.duration = durationOfShake; return shakeAnimation; }
64位:
static int numberOfShakes = 4; static float durationOfShake = 0.1f; static float vigourOfShake = 0.02f; -(void)shakeWindow:(NSWindow *)window { NSRect frame = [window frame]; CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"frame"]; NSRect rect1 = NSMakeRect(NSMinX(frame) - frame.size.width * vigourOfShake, NSMinY(frame), frame.size.width, frame.size.height); NSRect rect2 = NSMakeRect(NSMinX(frame) + frame.size.width * vigourOfShake, NSMinY(frame), frame.size.width, frame.size.height); NSArray *arr = [NSArray arrayWithObjects:[NSValue valueWithRect:rect1], [NSValue valueWithRect:rect2], nil]; [animation setValues:arr]; [animation setDuration:durationOfShake]; [animation setRepeatCount:numberOfShakes]; [window setAnimations:[NSDictionary dictionaryWithObject:animation forKey:@"frame"]]; [[window animator] setFrame:frame display:NO];