何事もなかったように、しれっと更新もぐもぐ

 

現在はCALayerに取り組んでいます。

以前UIViewの回転で手を焼いた部分も、

CoreAnimationを使えばシンプルに書くことができそうです。

 

今回ハマったのは、アニメーションのタイミングを操作する

CAMediaTimingプロトコルの、speed と beginTime、timeOffset の関係です。

 

普通、例えばタップイベントでspeedを2.0にしてやればそこから倍速に、

0.0にすれば止まってくれそうな気がしますが、実際はそうはいきませんゲッソリ

 

事のはじまりは、アニメーションの一時停止と再開でした。

Appleのドキュメントに次のコード(のObjective-C版)が載っています。

func pauseLayer(_ layer: CALayer) {
        let pauseTime = layer.convertTime(CACurrentMediaTime(), from: nil)
        layer.speed = 0.0
        layer.timeOffset = pauseTime
}
func resumeLayer(_ layer: CALayer) {
        let pauseTime = layer.timeOffset
        layer.speed = 1.0
        layer.timeOffset = 0.0
        layer.beginTime = 0.0
        layer.beginTime = layer.convertTime(CACurrentMediaTime(),from: nil) - pauseTime
}

これが読めなかったのです。何をやっているんだ?とポーン

特に最後のbeginTimeはなぜ一度0.0を入れているのか、と。

 

ようやくわかったので、忘れないように図にしてみました。

渾身のタイムチャートですグー

まずspeed属性の概念から。

speedを指定するとそこから先だけでなく、そこまでの時間軸も全て変わります。

例えば0.0にすると、時間軸の最初から永遠に0なので、初期状態のままになります。

2.0にすると開始時間も半分に早まり、0.5にすると2倍先まで待たされます。

ParentTimeはとても大きい数字になりうるので、実際には動かないも同然です。

 

そこで、beginTimeやtimeOffsetを使ってこの時間軸をずらします。

beginTimeは親要素の時間軸に則って与えられる数値です。

つまりspeed属性の影響を受けません。

一方 timeOffsetは、speed属性を反映した時間軸を操作します。

正数の場合、beginTimeは先延ばし、timeOffsetはたぐり寄せる側にずれます。

convertTime(_:from:)は、それぞれの時間を渡し合う変換器です。

 

ここで、あらためて一時停止と再開の仕組みを見てみます。

(a)はspeedのみで操作した場合です。アニメーションは止まりますが、

時間軸はゼロ…つまり初期状態に戻ってしまい、一時停止と言えません。

(b)はtimeOffsetを使って24を初期状態にすることで一時停止らしくなりましたが、

再開しようとすると、終了点が過去になるので、たちまち完了してしまいます。

(c)は再開時にtimeOffsetを元に戻すことで再びアニメーションが動き出しますが、

一時停止していなかったかのように元の時間軸で完了してしまいます。

 

(d)は再開時にtimeOffsetを元に戻し、さらにbeginTimeを使って、

一時停止していたぶんだけ時間軸を遅らせます。これがサンプルコードの動きです。

 

 beginTime = 0.0 としていたのは、一時停止していたぶんを知るために、

一時停止しなかった場合の時間(p)を得るためです。

 

 

理解はできましたが、結構わかりにくい仕組みですショボーン

ムービー編集ソフトのような親切なインタフェースではありませんね汗