web-dev-qa-db-ja.com

iPhoneのゆらゆらアイコン効果を作成する方法?

私はあなたがそれを押したときにiPhoneのアイコンが揺れるのと同じように私のアプリケーションで画像を前後に揺らしたいです。それを行う最良の方法は何ですか?

これは、アニメーションGIFを使用しないアニメーションへの私の最初の進出です。アイデアは、画像を少し前後に回転させて、ぐらつき効果を作り出すことだと思います。 CABasicAnimationおよびCAKeyframeAnimationの使用を見てきました。 CABasicAnimationは、開始位置にジャンプし、内挿しないため、繰り返すたびにジッタを作成します。 CAKeyframeAnimationは、私がそれを機能させることができないことを除いて、ソリューションのように思えます。私は何かが欠けているに違いありません。 CAKeyframeAnimationを使用したコードは次のとおりです(機能しません)。

    NSString *keypath = @"wobbleImage";
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:keypath];
animation.duration = 1.0f;
animation.delegate = self;
animation.repeatCount = 5;

CGFloat wobbleAngle = 0.0872664626f;
NSValue *initial = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(0.0f, 0.0f, 0.0f, 1.0f)];
NSValue *middle = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(wobbleAngle, 0.0f, 0.0f, 1.0f)];
NSValue *final = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(-wobbleAngle, 0.0f, 0.0f, 1.0f)];
animation.values = [NSArray arrayWithObjects:initial, middle, final, nil];

[imageView.layer addAnimation:animation forKey:keypath];


または、私が今見逃している完全にシンプルなソリューションがあるかもしれません。ポインターに感謝します。ありがとう!

52
jeanniey

簡単な方法:

#define RADIANS(degrees) (((degrees) * M_PI) / 180.0)

CGAffineTransform leftWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5.0));
CGAffineTransform rightWobble = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5.0));

itemView.transform = leftWobble;  // starting point

[UIView beginAnimations:@"wobble" context:itemView];
[UIView setAnimationRepeatAutoreverses:YES]; // important
[UIView setAnimationRepeatCount:10];
[UIView setAnimationDuration:0.25];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(wobbleEnded:finished:context:)];

itemView.transform = rightWobble; // end here & auto-reverse

[UIView commitAnimations];

...

- (void) wobbleEnded:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context 
{
     if ([finished boolValue]) {
        UIView* item = (UIView *)context;
        item.transform = CGAffineTransformIdentity;
     }
}

おそらくタイミングと角度で遊ぶ必要がありますが、これはあなたが始めるはずです。

編集:応答を編集して、完了時にアイテムを元の状態に戻すコードを追加しました。また、beginAnimationsコンテキスト値を使用して、start/stopメソッドに何でも渡すことができることに注意してください。この場合、それはウォブリングオブジェクト自体なので、特定のivarに依存する必要はなく、メソッドは任意の汎用UIViewベースのオブジェクト(テキストラベル、画像など)に使用できます。

92
Ramin

Raminの答えは非常に良かったが、OS4からは、1つの単純な関数でanimateWithDurationを使用しても同じ効果が得られる。

(彼の例を将来のグーグルに適合させた)

#define RADIANS(degrees) (((degrees) * M_PI) / 180.0)

- (void)startWobble {
 itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(-5));

 [UIView animateWithDuration:0.25 
      delay:0.0 
      options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionAutoreverse)
      animations:^ {
       itemView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, RADIANS(5));
      }
      completion:NULL
 ];
}

- (void)stopWobble {
 [UIView animateWithDuration:0.25
      delay:0.0 
      options:(UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveLinear)
      animations:^ {
       itemView.transform = CGAffineTransformIdentity;
      }
      completion:NULL
  ];
}
92
Pieter

CAKeyframeAnimationを使用して、よりスムーズなアニメーションを作成する必要があります。

+ (void) animationKeyFramed: (CALayer *) layer 
                   delegate: (id) object
              forKey: (NSString *) key {

    CAKeyframeAnimation *animation;
    animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
    animation.duration = 0.4;
    animation.cumulative = YES;
    animation.repeatCount = 2;
    animation.values = [NSArray arrayWithObjects:
            [NSNumber numberWithFloat: 0.0], 
            [NSNumber numberWithFloat: RADIANS(-9.0)], 
            [NSNumber numberWithFloat: 0.0],
            [NSNumber numberWithFloat: RADIANS(9.0)],
            [NSNumber numberWithFloat: 0.0], nil];
    animation.fillMode = kCAFillModeForwards;
    animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    animation.removedOnCompletion = NO;
    animation.delegate = object;

    [layer addAnimation:animation forKey:key];
}
14
docchang

ホーム画面のぐらつきとアイコンの動きを再現しようとするサンプルアプリを作成しました: iPhone Sample Code:Tiles

9

この投稿に最近出くわし、Swiftで同じことをしたい人のために、ここに私の翻訳があります:

func smoothJiggle() {

    let degrees: CGFloat = 5.0
    let animation = CAKeyframeAnimation(keyPath: "transform.rotation.z")
    animation.duration = 0.6
    animation.cumulative = true
    animation.repeatCount = Float.infinity
    animation.values = [0.0,
        degreesToRadians(-degrees) * 0.25,
        0.0,
        degreesToRadians(degrees) * 0.5,
        0.0,
        degreesToRadians(-degrees),
        0.0,
        degreesToRadians(degrees),
        0.0,
        degreesToRadians(-degrees) * 0.5,
        0.0,
        degreesToRadians(degrees) * 0.25,
        0.0]
    animation.fillMode = kCAFillModeForwards;
    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
    animation.removedOnCompletion = true

    layer.addAnimation(animation, forKey: "wobble")
}

func stopJiggling() {
    jiggling = false
    self.layer.removeAllAnimations()
    self.transform = CGAffineTransformIdentity
    self.layer.anchorPoint = CGPointMake(0.5, 0.5)
}
3
Allen Conquest

私が知っている最も簡単な方法は、Core Animationを使用することです。基本的に、コアアニメーションブロックを作成してから、回転変換を行い、カウントを設定して繰り返します。その後、Core Animationが、このウォブリングエフェクトを実行するために必要なすべてを処理します。

Core Animationブロックを開始するには、次を実行します。

[UIView beginAnimations:@"any string as animationID" context:self];
[UIView setAnimationRepeatCount:10];
// rotate 
[UIView commitAnimations];

未検証。ただし、次のことも必要になる場合があります。

[UIView setAnimationBeginsFromCurrentState:YES];

私の知る限り。 setAnimationRepeatCountには、指定した回数だけアニメーションが完了、元に戻す、完了、元に戻す、完了、元に戻す、完了...という効果があります。したがって、最初に繰り返しカウントなしで左に回転し、このポイントから繰り返しカウントでウォブリングを開始することができます。完了したら、回転してアイデンティティ変換に戻します(=回転とスケーリングは適用されません)。

アニメーションデリゲートを次のように設定することにより、アニメーションをチェーンできます。

[UIView setAnimationDelegate:self]

その後

[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];

アニメーションが停止するとすぐに、そのメソッドが呼び出されます。アニメーションが停止したときに呼び出されるメソッドを実装する方法については、UIViewクラスのドキュメントを参照してください。基本的に、そのメソッド内で、新しいアニメーションブロックで同じコンテキストとアニメーションIDを使用して次のステップ(つまり、回転バックなど)を実行し、(必要に応じて)別のdidStopSelectorを指定します。

更新:

あなたはチェックアウトしたいかもしれません:

[UIView setAnimationRepeatAutoreverses:YES];

これは自動的に行き来します。

2
Thanks

CAKeyframeAnimationを使用して、次のように無限のウォブル効果を作成できます。

CGFloat degrees = 8.0;
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.duration = 0.6;
animation.cumulative = YES;
animation.repeatCount = 1;
animation.values = @[@0.0,
                    @RADIANS(-degrees) * 0.25,
                    @0.0,
                    @RADIANS(degrees) * 0.5,
                    @0.0,
                    @RADIANS(-degrees),
                    @0.0,
                    @RADIANS(degrees),
                    @0.0,
                    @RADIANS(-degrees) * 0.5,
                    @0.0,
                    @RADIANS(degrees) * 0.25,
                    @0.0];
animation.fillMode = kCAFillModeForwards;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
animation.removedOnCompletion = YES;

[self.layer addAnimation:animation forKey:@"wobble"];
2
Berik

パーティーに遅れて。私は通常、減衰機能付きのスプリングを使用しています(iOS7以降)。 Swiftのようになります:

    sender.transform = CGAffineTransformMakeScale(1.2, 1.2)
    UIView.animateWithDuration(0.30, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.3, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in
        sender.transform = CGAffineTransformMakeScale(1, 1)
    }) { (Bool) -> Void in
    //Do stuff when animation is finished
    }

initialSpringVelocitySpringWithDampingを調整することにより、効果を微調整できます。

1
EsbenB

EsbenBの回答に基づきますが、Swift 3およびローテーション:

    sender.transform = CGAffineTransform(rotationAngle: 12.0 * .pi / 180.0)
    UIView.animate(withDuration: 0.60, delay: 0.0, usingSpringWithDamping: 0.3, initialSpringVelocity: 0.3, options: .curveEaseInOut, animations: { () -> Void in
        sender.transform = CGAffineTransform(rotationAngle: 0.0)
    }, completion: nil)
1
user599849

Ramin で指定されたコードは正常に機能しますが、タブバーアプリケーションを使用して次のタブ項目に移動し、前のタブ項目に戻ると、ビューがすべて左に移動していることがわかりますしたがって、ベストプラクティスは、ViewWillAppearメソッドを使用することです。

- (void)viewWillAppear:(BOOL)animated
{
    UIView* item = self.view;
    item.transform = CGAffineTransformIdentity;
}

ビューがロードされるたびに、適切な場所でアニメーションが見つかります。このメソッドも使用します。

[UIView setAnimationDidStopSelector:@selector(myMethod:finished:context:)];
0
Sabby

Swift 4+でこれを行う最も簡単な方法:

static func wobbleAnimation(button: UIButton) {
    CATransaction.begin()
    let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
    rotateAnimation.autoreverses = true
    rotateAnimation.repeatCount = Float.greatestFiniteMagnitude
    rotateAnimation.fromValue = CGFloat(-0.2)
    rotateAnimation.toValue = CGFloat(0.2)
    rotateAnimation.duration = 0.20
    button.layer.add(rotateAnimation, forKey: nil)
    CATransaction.commit()
}

static func stopWobbleAnimation(button: UIButton) {
    button.layer.removeAllAnimations()
}
0
Coder ACJHP

Swift

func startWiggling() {
    deleteButton.isHidden = false
    guard contentView.layer.animation(forKey: "wiggle") == nil else { return }
    guard contentView.layer.animation(forKey: "bounce") == nil else { return }

    let angle = 0.04

    let wiggle = CAKeyframeAnimation(keyPath: "transform.rotation.z")
    wiggle.values = [-angle, angle]
    wiggle.autoreverses = true
    wiggle.duration = randomInterval(0.1, variance: 0.025)
    wiggle.repeatCount = Float.infinity
    contentView.layer.add(wiggle, forKey: "wiggle")

    let bounce = CAKeyframeAnimation(keyPath: "transform.translation.y")
    bounce.values = [4.0, 0.0]
    bounce.autoreverses = true
    bounce.duration = randomInterval(0.12, variance: 0.025)
    bounce.repeatCount = Float.infinity
    contentView.layer.add(bounce, forKey: "bounce")
}

func stopWiggling() {
    deleteButton.isHidden = true
    contentView.layer.removeAllAnimations()
}
0
Beslan Tularov