LoginSignup
7
6

More than 5 years have passed since last update.

removedOnCompletionをfalseにするならanimationDidStopでremoveAnimationを実行した方が良い(かも)

Last updated at Posted at 2015-01-01

16/11/12 修正

iOS10から既存のanimationDidStopは廃止になりました。

QuartzCore Changes for Swift

今まではNSObjectのメソッドでしたが、今後はCAAnimationのメソッドになります。
よってanimationDidStopの呼び出しも変更になります。

class ViewController: UIViewController, CAAnimationDelegate{ //<- CAAnimationのDelegateが追加が必要

今まではDelegateの指定が要らなかったのですが、今度から必要です。

func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {

}

overrideの指定も不要です。
以上、変更点でした。

16/03/14 タイトル及び本文修正

CABasicAnimationで変更した結果のままにしたい場合は以下のようにします。

let animateStrokeEnd = CABasicAnimation(keyPath: "progress")
animateStrokeEnd.delegate = self // animationDidStopを呼び出すのに必須
animateStrokeEnd.fromValue = fromValue
animateStrokeEnd.toValue = toValue 
animateStrokeEnd.removedOnCompletion = false
animateStrokeEnd.fillMode = kCAFillModeForwards
_sublayer?.addAnimation(animateStrokeEnd, forKey: "progress")

しかし、この状態はアニメーションの処理がずっと動きっぱなしの状態でしかも実際にはCALayerの値を変更してるわけではないようです。
なので、アニメーションを止める且つCALayerの値を実際に変更してやる必要があるけど、animationDidStopで実行してやると丁度良いのではないかと。

    override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
        _sublayer?.propery = toValue
        _sublayer?.removeAnimationForKey("progress")  //こちらか下か
        _sublayer?.removeAllAnimations() // どちらか都合の良い方を使う
    }

以上です。書きたかった事をまとめればえらくシンプルになりました。

下記は修正前15/10/10時点の記事。備忘録でとりあえず残しておきます。


ちょっとまだ整理仕切れてないけど。
CABasicAnimationとUIBezierPathを使ったアニメーションを試していたところ、CABasicAnimationのアニメーションとはCALayerのdrawInContextをプロパティのfromValueからtoValueまで再描画を繰り返す動きをしている事だと分かった。
(CALayerのサブクラスでdrawInContextでログを出すようにでもして貰ったら分かります。)
ところで、CABasicAnimationはアニメーションが終了したら、描画した内容がクリアされる問題があって、それを解決するにはプロパティのremovedOnCompletionとfillModeをセットで設定してやる。こんな形で。


let animateStrokeEnd = CABasicAnimation(keyPath: "progress")
animateStrokeEnd.fromValue = fromValue
animateStrokeEnd.toValue = toValue            
animateStrokeEnd.removedOnCompletion = false
animateStrokeEnd.fillMode = kCAFillModeForwards

ところがプロパティのremovedOnCompletionを使うとtoValueまでの値になっても再描画の動きが止まらない事が判明。removedOnCompletionという単語の意味を考えればそうなってもおかしくないのだが、最近まで全然気付いてなかった・・・。
なんにせよ、removedOnCompletionを使うとずっとアニメーションそのものを削るかそのアニメーションをしているCALayerそのものを削除しない限り、処理が動きっぱなしだと言う事。それはあまり良くないので、アニメーションを削るか、CALayerを削除する処理をしないならremovedOnCompletionは使わない方がよい。
その代わりの処理としては以下のような方法がいいみたいです。

/* 別のファイルもしくは上記でサブレイヤークラスを作成 */

var _subLayer : SubLayer? = nil

/* 省略 */

func moveLayer {
let animateStrokeEnd = CABasicAnimation(keyPath: "progress")
animateStrokeEnd.fromValue = fromValue
animateStrokeEnd.toValue = toValue            
//animateStrokeEnd.removedOnCompletion = false コメントアウト
animateStrokeEnd.fillMode = kCAFillModeForwards
animateStrokeEnd.delegate = self //デリゲートを設定
_sublayer?.addAnimation(animateStrokeEnd, forKey: "progress")

}

override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
let pinnedProgressNumber : CGFloat = anim.valueForKey("toValue") as CGFloat
_sublayer?.toValue = pinnedProgressNumber        

}

CABasicAnimationのデリゲートをセットし、アニメーションの終了時に呼び出されるメソッドをオーバーライド。その中で、アニメーション後の値をセットしてやる。例えば移動のアニメーションなら移動先の座標など。

以上です。


7
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6