web-dev-qa-db-ja.com

iPhoneで視覚効果を振る(デバイスを振らない)

ログインに失敗した場合、私はアラートを表示しないようにしたいと思います。アラートを表示してから、ログイン画面のどこかにテキストを表示すると、重複しているように見えます。

そのため、Macのログイン画面のように、ユーザーが間違ったユーザーIDとパスワードを入力したときに、ログインビューをグラフィカルに揺らしたいと思います。

これを取り除く方法があるかどうか、または私が使用できる別の効果について何か提案はありますか?

44
Steven Fisher

私はこれがより効率的な解決策だと思います:

迅速:

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 ] ;

作成されるアニメーションオブジェクトは1つだけで、すべてCoreAnimationレベルで実行されます。

97
nielsbot

IOS 4以降のブロックベースのUIKitアニメーションを使用する(おおまかにjayccrownの回答に基づく):

- (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
Chris Miles

私はいくつかのぐらつきアニメーションを見て、ビューをtピクセル上下に振るように変更しました:

- (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
jayccrown

Cocoaでの実行方法を詳しく説明したチュートリアルを次に示します。 iPhoneでも同じである必要があります(または少なくともかなり似ています)。

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

8

ビューの中央プロパティのX座標を変更するだけでうまくいく場合があります。コアアニメーションをまったく実行していない場合は、かなり単純です。

最初に、アニメーションを右に開始し、それが終了するのを聞いてから、左に戻します。 「適切に感じる」ようにタイミングを下げるには、しばらく時間がかかる場合があります。

- (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
slf

このUIViewカテゴリスニペットは私にとってはうまくいきました。ビューのレイヤーに適用された3つのCABasingAnimationsを使用しています。

#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

それが誰かを助けることを願っています:)

4
JakubKnejzlik

IOS 7.0以降では、UIKitキーフレームアニメーションを使用できます。

[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
ishkawa

質問はすでに回答済みですが、以前にこのようなものをすでに実装しているので、追加しても害はないと感じています。

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"];

また、ユーザーがログインに失敗したことをシェイクで示す必要があるため、画面がシェイクしている間、画面が赤く染まるこのアニメーションを追加することも検討してください。

//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"];

お役に立てれば!

1
pasawaya

uIViewの非常に簡単なシェイクカテゴリ

https://github.com/jonasschnelli/UIView-I7ShakeAnimation

1
Jonas Schnelli

ストーリーボードで設定した制約に使用したソリューション。 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
    }
}

私の場合、アニメーションは継続しましたが、数秒後にビューコントローラーをポップすると、タイマーも停止します。

0
Alserda

自動レイアウトを使用して、Chris Milesの回答を調整しましたが、次のようにNSLayoutConstraintsをアニメーション化しました。

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
jay492355