web-dev-qa-db-ja.com

Objective-C-アニメーションの後に変更を適用するCABasicAnimation?

CABasicAnimationを使用して、画像ビューの移動とサイズ変更を行っています。画像ビューをスーパービューに追加してアニメーション化し、スーパービューから削除したい。

それを達成するために、私はCAAnimationGroupのデリゲート呼び出しをリッスンしており、呼び出されたらすぐにスーパービューから画像ビューを削除します。

問題は、スーパービューから削除される前に、最初の場所で画像が点滅する場合があることです。この動作を回避する最良の方法は何ですか?

CAAnimationGroup *animGroup = [CAAnimationGroup animation];
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, scaleAnim, opacityAnim, nil];
    animGroup.duration = .5;
    animGroup.delegate = self;
    [imageView.layer addAnimation:animGroup forKey:nil];
60
aryaxt

アニメーションをレイヤーに追加しても、アニメーションはレイヤーのプロパティを変更しません。代わりに、システムはレイヤーのコピーを作成します。元のレイヤーはモデルレイヤーと呼ばれ、複製はプレゼンテーションレイヤーと呼ばれます。プレゼンテーションレイヤーのプロパティはアニメーションの進行に伴って変化しますが、モデルレイヤーのプロパティは変更されません。

アニメーションを削除すると、システムはプレゼンテーションレイヤーを破棄し、モデルレイヤーのみを残します。モデルレイヤーのプロパティは、レイヤーの描画方法を制御します。そのため、モデルレイヤーのプロパティがプレゼンテーションレイヤーのプロパティの最終的なアニメーション値と一致しない場合、レイヤーはアニメーションの前の外観に即座にリセットされます。

これを修正するには、モデルレイヤーのプロパティをアニメーションの最終値に設定し、thenアニメーションをレイヤーに追加する必要があります。レイヤープロパティを変更すると、プロパティにimplicitアニメーションが追加され、明示的に追加するアニメーションと競合する可能性があるため、この順序で行う必要があります。明示的なアニメーションが暗黙的なアニメーションをオーバーライドすることを確認する必要があります。

では、これをどのように行うのですか?基本的なレシピは次のようになります。

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.fromValue = [NSValue valueWithCGPoint:myLayer.position];
layer.position = newPosition; // HERE I UPDATE THE MODEL LAYER'S PROPERTY
animation.toValue = [NSValue valueWithCGPoint:myLayer.position];
animation.duration = .5;
[myLayer addAnimation:animation forKey:animation.keyPath];

アニメーショングループを使用したことがないため、変更する必要があるものが正確にはわかりません。各アニメーションをレイヤーに個別に追加します。

また、+[CATransaction setCompletionBlock:]メソッドは、アニメーションのデリゲートを使用しようとする代わりに、1つまたは複数のアニメーションの完了ハンドラーを設定します。トランザクションの完了ブロックを設定してから、アニメーションを追加します。

[CATransaction begin]; {
    [CATransaction setCompletionBlock:^{
        [self.imageView removeFromSuperview];
    }];
    [self addPositionAnimation];
    [self addScaleAnimation];
    [self addOpacityAnimation];
} [CATransaction commit];
185
rob mayoff

CAAnimationsは完了すると自動的に削除されます。これを制御するプロパティremovedOnCompletionがあります。 NOに設定する必要があります。

さらに、fillModeとして知られているものがあり、これはアニメーションの継続時間の前後の動作を制御します。これは、CAMediaTimingCAAnimationに準拠)で宣言されたプロパティです。これをkCAFillModeForwardsに設定する必要があります。

これらの両方の変更により、アニメーションは完了後も持続するはずです。ただし、グループで変更する必要があるか、グループ内の個々のアニメーションで変更する必要があるか、またはその両方が必要かどうかはわかりません。

33
Lily Ballard

Swiftの例は誰かを助けるかもしれません

グラデーションレイヤー上のアニメーションです。 _.locations_プロパティをアニメーション化しています。

@robMayoffの回答が完全に説明している重要なポイントは、次のとおりです。

驚くべきことに、レイヤーアニメーションを行うとき、実際にアニメーションを開始する前に、最初に最終値を設定します。

以下は、アニメーションが無限に繰り返されるため、良い例です。

アニメーションが際限なく繰り返されるとき、「アニメーションを設定する前に値を設定するのを忘れる」という古典的な間違いを犯すと、アニメーションの間に「フラッシュ」がときどき表示されます。

_var previousLocations: [NSNumber] = []
...

func flexTheColors() { // "flex" the color bands randomly

    let oldValues = previousTargetLocations
    let newValues = randomLocations()
    previousTargetLocations = newValues

    // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!"
    theLayer.locations = newValues

    // AND NOW ANIMATE:
    CATransaction.begin()

    // and by the way, this is how you endlessly animate:
    CATransaction.setCompletionBlock{ [weak self] in
        if self == nil { return }
        self?.animeFlexColorsEndless()
    }

    let a = CABasicAnimation(keyPath: "locations")
    a.isCumulative = false
    a.autoreverses = false
    a.isRemovedOnCompletion = true
    a.repeatCount = 0

    a.fromValue = oldValues
    a.toValue = newValues

    a.duration = (2.0...4.0).random()

    theLayer.add(a, forKey: nil)
    CATransaction.commit()
}
_

以下は、新しいプログラマにとって何かを明確にするのに役立つかもしれません。私のコードではこれを行うことに注意してください:

_    // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!"
    theLayer.locations = newValues

    // AND NOW ANIMATE:
    CATransaction.begin()
    ...set up the animation...
    CATransaction.commit()
_

ただし、他の回答のコード例では、次のようになっています。

_    CATransaction.begin()
    ...set up the animation...
    // IN FACT, ACTUALLY "SET THE VALUES, BEFORE ANIMATING!"
    theLayer.locations = newValues
    CATransaction.commit()
_

「アニメートする前に値を設定する」コード行の位置について..

実際には、その行実際に「内部」のコードの開始行を持つことは完全に問題ありません。 .commit()の前に行う限り。

新しいアニメーターを混乱させる可能性があるため、これについてのみ言及します。

1
Fattie