Shake visual effect on iPhone (NOT shaking the dev

2019-01-10 00:57发布

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?

11条回答
劳资没心,怎么记你
2楼-- · 2019-01-10 01:12

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.

查看更多
Luminary・发光体
3楼-- · 2019-01-10 01:15

Using Auto Layout, I adapted Chris Miles' answer but animated NSLayoutConstraints like this:

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];
    }
}];
查看更多
来,给爷笑一个
4楼-- · 2019-01-10 01:16

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 :)

查看更多
We Are One
5楼-- · 2019-01-10 01:18
我欲成王,谁敢阻挡
6楼-- · 2019-01-10 01:20

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];
  }
}
查看更多
Viruses.
7楼-- · 2019-01-10 01:30

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];
查看更多
登录 后发表回答