ios - keypath - swift アニメーション



モデルレイヤを更新するとCABasicAnimationが正しくアニメーション化されない (1)

私は現在、 CALayer transformプロパティをアニメーション化するCABasicAnimationを実装しています。 今、私はCore Animationを初めて使っていますが、objc.ioなどのさまざまなブログや記事を通して、アニメーションをスティッキングするために頻繁に(誤って)推奨される方法を使用することは非常に悪い考えです。 fillModeおよびremovedOnCompletionプロパティ。 このメソッドは、モデルレイヤーとプレゼンテーションレイヤーの間に相違が生じ、そのレイヤーの1つへの将来のクエリーがユーザーが見ているものと一致しない可能性があるため、多くの人にとっては悪い習慣とみなされます。

代わりに、アニメーションを行う推奨される方法は、アニメートされるレイヤーにアニメーションを追加すると同時にモデルレイヤーを更新することです。 しかし、これがどのように機能しているかを正確に理解することができません。 私のアニメーションはシンプルで、次のようになります:

CATransform3D updatedTransform = [self newTransformWithCurrentTransform];
// Location 1
CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
transformAnimation.duration = 1;
transformAnimation.fromValue = [NSValue valueWithCATransform3D:self.layerBeingAnimated.transform]; // Does not work without this.
transformAnimation.toValue = [NSValue valueWithCATransform3D:updatedTransform];
// Location 2
[self.layerBeingAnimated addAnimation:transformAnimation forKey:kTransformAnimationKey];
// Location 3

私は、コードを使ってモデルレイヤーを更新しようとした3つの場所を示しました

self.layerBeingAnimated.transform = updatedTransform;

位置1では、レイヤーはnewTransform右にジャンプし、アニメーション化されません。 位置2では、レイヤーは、現在のトランスフォームからnewTransformへと正確にアニメーション化されnewTransform 。 位置3では、レイヤーがnewTransformに右にジャンプし、古いトランスフォームにジャンプし、fromValueからnewTransformに正しくアニメーションされ、newTransformにとどまります。

ここで何が取引ですか? モデルレイヤーを更新する正しい場所は何ですか?また、これらの3つの場所で異なる結果が生じるのはなぜですか?

ありがとう!


私は3つの場所のそれぞれについて何が起こっているのかを説明するのが最も簡単だと思いますし、最後に「結論」を説明します。

私はまた、いくつかのイラストレーションを加えて、あなたがあなたの質問に言及している振る舞いを正確に示しているので、これらの3つのことを試していない人のためにフォローするのが簡単になります。 また、スタンドアローンレイヤーとバッキングレイヤー(ビューに添付されているレイヤー)の両方を表示するようにイラストを拡張しています。

ロケーション1

最初の場所では、アニメーションが作成される前にモデル値が更新されます。 これが完了すると、transformプロパティはupdatedTransformを保持します。 つまり、fromValueのレイヤーからトランスフォームを読み取ると、updatedValueが戻されます。 これは、toとvalueの両方が同じであるため、アニメーションを見ることができないことを意味します。

この位置を期待どおりに機能させることができることの1つは、新しい値を割り当てる前にoldValueを読み取り、それをfromValueとして使用することです。 これは期待通りに見えます。

// Location 1
CATransform3D oldValue = layer.transform; // read the old value first
layer.transform = updatedTransform;       // then update to the new value

CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:@"transform"];
anim.duration  = 1.0;
anim.fromValue = [NSValue valueWithCATransform3D:oldValue];
anim.toValue   = [NSValue valueWithCATransform3D:updatedTransform];

ロケーション2

2番目の例では、from値の変換を読み取ったときに値が更新されていないため、fromValueとtoValueは異なります。 その後、モデル値は最終値に更新されます。 スタンドアローンレイヤーとバッキングレイヤーの違いは実際にはありますが、わかりません。 CALayerのtransformプロパティはアニメーション化可能で、値が変更されると自動的に「暗黙の」アニメーションを実行します。 これは、 "トランスフォーム"キーパスのレイヤーにアニメーションが追加されることを意味します。 ただし、このビューはアニメーションブロックの外側で変更が発生したときにこの動作を無効にするため、暗黙のアニメーションは存在しません。

暗黙のアニメーションが表示されない理由は、後で同じキーパスに対して「明示的」アニメーションが追加されるためです。 これは、明示的なアニメーションだけが表示されることを意味します。スタンドアロンレイヤーで2つのアニメーションが実行されていると思われる場合でも(後で詳しく説明します)、どちらの場合も表示されます。 注意が必要な場合は、スタンドアロン層の暗黙のアクションを無効にすることができます(詳細は後で説明します)。

場所3

これは最後の場所に私たちを残します。 この場合、アニメーションは上記のように、fromValueとtoValueが異なるように作成されます。 唯一の違いは、明示的なアニメーションを追加し、暗黙のアニメーションをトリガーするプロパティを変更する順序です。 この場合、暗黙のアニメーションは明示的なアニメーションの後に追加され、両方とも実行されます(!)。 どちらのアニメーションも実際には位置2で実行されましたが、明示的な(より長い)アニメーションが前に追加されたため、アニメーションを見ることができませんでした。

すべてが非常に速く動いているので、2つのアニメーションが同時に実行されているときに何が起きているのかを試してみるために、レイヤ全体を遅くしました。 このようにすれば、暗黙のアニメーションが終了したときに何が起こるかを見るのがはるかに簡単になります。 私はうまく動作するバッキングレイヤーと誤動作しているスタンドアロンレイヤーを重ね合わせて、両方を50%透明にしました。 破線の輪郭が元のフレームです。

何が起こっているのかの簡単な説明:青いビューはそれに追加された明示的なアニメーション(1秒の持続時間を持つ)のみを取得します。 オレンジ色のレイヤーには最初に同じ明示的なアニメーションが追加され、暗黙の0.25秒のアニメーションが追加されます。 明示的アニメーションも暗黙的アニメーションも「加法的」ではないため、toValueおよびfromValueはそのまま使用されます。

免責事項:私はAppleで働いていないし、Core Animationのソースコードを見ていないので、私が言うことは、物事どう動くかに基づいて推測することです。

私の理解では(これは免責事項を参照してください)、これはアニメーションを生成するすべての画面のリフレッシュで起こります。現在のタイムスタンプでは、レイヤーはアニメーションが追加された順番に進み、プレゼンテーション値が更新されます。 この場合、明示的なアニメーションは回転変換を設定し、次に暗黙のアニメーションが来て、明示的な変換を完全にオーバーライドする別の回転変換を設定します。

アニメーションが「付加的」に設定されている場合は、上書きする代わりにプレゼンテーションの値に追加されます(これは超強力です)。 アディティブアニメーションでも、順序はまだ問題ありません。 非付加的なアニメーションは、後に来て全部を上書きすることができます。

暗黙的なアニメーションは明示的なアニメーションよりも短いので、全体のアニメーションの最初の部分については、厳密に暗黙的なアニメーション(最後に追加されたもの)から来ていることがわかります。 暗黙的なアニメーションが終了すると、残りのアニメーションは暗黙のアニメーションの下で実行されている明示的なアニメーションだけです。 暗黙的なアニメーションが終了すると、明示的なアニメーションはすでに0.25秒進んでおり、オレンジ色のレイヤーは最初に戻るのではなく、青色のビューと同じ値にジャンプします。

どこ値を更新する必要がありますか?

この時点で、問題は、2つのアニメーションが追加されないようにするにはどうすればよいのですか? 値が更新された場所では、2つのアニメーションが表示されることはありません(ただし、最終結果の外観に影響する可能性があります)。

スタンドアローンレイヤーに2つのアクションが追加されないように、すべての「アクション」(アニメーションのより一般的な用語)を一時的に無効にします。

[CATransaction begin];
[CATransaction setDisableActions:YES]; // actions are disabled for now
layer.transform = updatedTransform;
[CATransaction commit];                // until here

これを行うと、レイヤーにアニメーションが1つだけ追加されるため、位置2または3のいずれかが機能します。 それは単に味の問題です。 oldValueを読み込んだ場合は、場所1を使用することもできます(アクションが無効である限り)。

バッキングレイヤをアニメートする場合は、アクションを無効にする必要はありません(ビューはそれを行います)が、それを行うことも害はありません。

この時点で、アニメーションを構成する他の方法、追加的なアニメーション、そしてなぜこの場合にtoValueとfromValueの両方を指定する必要があったのかについて、私は考えていました。 しかし、私はあなたが尋ねた質問に答えてくれたと思います。そして、この答えはすでに長いことです。





calayer