UIViewPropertyAnimatorの基本まとめ①

iOS10から使用できるようになったUIViewPropertyAnimatorによって、
簡単に様々なアニメーションを作成することができるようになりました。

カッコよくてオシャレなUIを作る上で、
アニメーションに関する技術を向上させることは必須かと思います。

そこで、実際にUIViewPropertyAnimatorの基本的な機能を使用してみて、
わかったこと、わからなかったことを整理する為にまとめておきます。

#基本的な構成

アニメーションは基本的に以下のような感じで実装できます。

````swift:ViewController.swift

class ViewController: UIViewController {
    // 使用する変数を宣言
    var circle: UIView! //アニメーションで動かすUIView
    var animator: UIViewPropertyAnimator! //animator

    override func viewDidLoad(){
        super.viewDidLoad()
        
        // circleを生成し、画面に表示
        circle = UIView(frame: CGRect(x:0, y: 0, width: 30, height: 30))
        circle.center.y = 200
        circle.center.x = self.view.center.x
        circle.layer.masksToBounds = true
        circle.layer.cornerRadius = 15
        circle.backgroundColor = UIColor.black
        
        self.view.addSubview(circle)

        // animatorを生成し、viewが下方向にスライドしていくアニメーションを追加する
        animator = UIViewPropertyAnimator(duration: 3.0, curve: .linear){
            self.circle.center.y += 200
        }
    }

    @IBAction func fallTapped(_ sender: UIButton) {
        //ボタンが押されたら、アニメーションがスタートする
        animator.startAnimation()
    }

    override func didReceiveMemoryWarning(){
        super.didReceiveMemoryWarning
    }
}
````

これでボタンを押すと、circleが落下するアニメーションができました！！
簡単ですねっ！！(*´∀`)♪

![vpa_gif01.gif](https://qiita-image-store.s3.amazonaws.com/0/135729/4b2abf67-bfd3-da75-b44c-fd0780fd5225.gif)

#UIViewAnimatingプロトコル

````swift:ViewController.swift
@IBAction func fallTapped(_ sender: UIButton) {
    //ボタンが押されたら、アニメーションがスタートする
    animator.startAnimation()
}
````
で呼び出している**startAnimationメソッド**は**[UIViewAnimationgプロトコル](https://developer.apple.com/reference/uikit/uiviewanimating)**で定義されているメソッドです。

UIViewAnimatingプロトコルは**アニメーションの開始、一時停止、終了などを制御するメソッドを定義していたり**、**アニメーションがactive、inactive、stoppedの内、どの状態なのかをという情報を取得したり**することができるプロトコルです。

UIViewPropertyAnimatorクラスは、UIViewAnimatingプロトコルを継承しているので、
アニメーションを開始する際に**startAnimationメソッド**を呼び出しています。

#アニメータ生成時のオプションの活用
UIViewPropertyAnimatorを生成するメソッドの種類はいくつかありますが、
curve、ベジエ曲線、dampingRatioなどを引数にとり、アニメーションの速度や性質などに変更を加えることが可能です。

いくつか例を挙げていきます。
###● curve: に設定する値でアニメーションの速度変更
curve:で設定するのは、[UIViewAnimationCurve](https://developer.apple.com/reference/uikit/uiviewanimationcurve)というenumerationの値であり、以下の4つの値を設定できます。

````swift:ViewController.swift
// animatorを生成し、viewが下方向にスライドしていくアニメーションを追加する
animator = UIViewPropertyAnimator(duration: 3.0, curve: .linear){
    self.circle.center.y += 200
}
````

|値|特徴|
|:------------------:|:------------------|
|.easeInOut|アニメーションの開始間際と終了間際がゆっくりになる。|
|.easeIn|アニメーションがゆっくりから開始され、徐々にスピードアップする。|
|.easeOut|アニメーションが速い状態で開始され、徐々にスピードダウンする。|
|.linear|アニメーションが均等の速さで実行される。|

公式リファレンスによれば、一般的にアニメーションで使用されることが多いのは.easeInOutだとのことですが、必要に応じて簡単に使い分けることができて便利ですね！

###● ベジエ曲線によるアニメーションの速度変更
ベジエ曲線を使用してアニメーションの速度を変更することも可能です。

````swift:ViewController.swift
// bezier曲線による速度制御
bezierAnimator = UIViewPropertyAnimator(duration: 1.0, controlPoint1: CGPoint(x: 0.1, y: 0.5), controlPoint2: CGPoint(x:0.5, y:0.2)){
    self.circle.center.y += 200
}
````

中間がゆっくりになっています！（わかりにくかったらすみません。）

![vpa_bezier_gif.gif](https://qiita-image-store.s3.amazonaws.com/0/135729/b014c388-1e67-c82a-a607-b86ea316a08c.gif)


ベジエ曲線は、人によっては全く馴染みの無い方もいらっしゃると思います。
筆者も恥ずかしながら全く知りませんでした。...orz

「ベジエ曲線とはなんぞや？」という方、以下のリンクでわかりやすく解説されていますので、ぜひ参照してみてください！！

[中学生でもわかるベジェ曲線](http://blog.sigbus.info/2011/10/bezier.html)

![step2.gif](https://qiita-image-store.s3.amazonaws.com/0/135729/2117dfdd-a356-f5d9-7389-26cba4ac6e80.gif)

[リンクのgifを引用]

↑↑こういうことだそうです！！

curveだと、既存の速度制御しかできませんが、ベジエ曲線を使用することによって、より開発者の意向に沿ったアニメーションを柔軟に作成することが可能です！！

ベジエ曲線の特徴を理解するのに時間がかかりそうですが、表現の幅が広がるので、時間をかけて損は無いと思います。

###● バネのようなアニメーションを簡単に設定
dampingRatioを使用すると、バネのようなアニメーションを簡単に設定することができます

````swift:ViewControlelr.swift
//バネのようなアニメーションを設定
dampingAnimator = UIViewPropertyAnimator(duration: 2.0, dampingRatio:0.2){
    self.circle.center.y += 150
}
````

実行すると以下のようになります！

![vpa_damping_gif.gif](https://qiita-image-store.s3.amazonaws.com/0/135729/737a923a-7ed0-2d43-080a-5d91e8906667.gif)


このように、ポップな動きを表現することが可能です！


例で挙げたように、使用するパラメータの種類を変更するだけで、
また、実引数を変更するだけで、簡単に複雑で繊細なアニメーションを作成することができてしまいました。

恐るべしっ！UIViewPropertyAnimator！Σ（ﾟдﾟlll）

#ユーザーのアクションに応じて動作を変更可能

続いて、ユーザーのアクションによって動作を変更するインタラクティブなアニメーションの実装について、
基本的な部分を見ていきます。

###● アニメーションを一時停止させる

アニメーションの一時停止もとても簡単です！
①一時停止する為のボタンを追加
②ボタンを押下した時のメソッドで**pauseAnimationメソッド**を呼び出す

````swift:ViewController.swift
@IBAction func pauseTapped(_ sender: Any) {
    //アニメーションを一時停止させるメソッドを呼び出す
    animator.pauseAnimation()
}
````
上記の2つの手順で実装できます。

![vpa_gif02.gif](https://qiita-image-store.s3.amazonaws.com/0/135729/895fe0b4-ad42-3cab-d157-66f628a3bdb5.gif)

Pause!ボタンを押下するとアニメーションが一時停止し、再度Fall!ボタンを押下すると、アニメーションが再開します！

<img width="475" alt="スクリーンショット 2017-01-15 14.17.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/135729/90e586b8-ae46-066e-706b-5481ba6f3e50.png">

仕組みとしては
startAnimationメソッドによってアニメーションの状態がActiveとなり、
PauseAnimationメソッドによってアニメーションの状態がInactiveとなっているということかと思います。

###● アニメーションを終了する

アニメーションの終了も手順は一時停止とほぼ同じです。

````swift:ViewController.swift
@IBAction func stopTapped(_ sender: UIButton) {
    //アニメーションを停止するメソッド
    animator.stopAnimation(false)
    //アニメーションを終了するメソッド
    animator.finishAnimation(at: .start)
}
````
これで完了です！

![vpa_03gif.gif](https://qiita-image-store.s3.amazonaws.com/0/135729/477ec539-5a09-4f08-3523-c5ef6225409e.gif)


stopAnimationの引数であるBool値は、
公式リファレンスによれば、

>**withoutFinishing**
>A Boolean indicating whether any final actions should be performed. Specify true to clear any animations and move the animator directly to the inactive state without performing any final actions. Specify false to put the animator into the stopped state.
>--[翻訳]--
>**Finishingなし**
>最後のアクションを実行する必要があるかどうかを示すブール値です。 trueを指定するとアニメーションが消去され、最後のアクションを実行せずにアニメーターを非アクティブ状態に直接移動できます。 アニメータを停止状態にするには、falseを指定します。

とのことです。
文章だけでは理解するのが難しかったので、実際にtrueを指定した時とfalseを指定した時の違いを検証してみました。

finishAnimation(at: .start)の**.start**の部分はfinalPositionパラメータであり、アニメーションが終了した時の最後の位置を表していて、以下の3つの値を設定することができます。

|値|UIViewの停止位置|
|:------------------:|:------------------|
|.start|UIViewがアニメーションが開始されたスタートの位置に表示されて停止する。|
|.end|UIViewがアニメーションが停止された時点の位置で停止する。|
|.current|UIViewがアニメーションが終了する位置に表示されて停止する。|

それぞれ3つの状態でアニメーションが終了しますが・・・。

stopAnimationメソッドのwithoutFinishingに指定する引数の値がtureだと、finishAnimation(at:)で指定した値が適用されず、常に.currentと同様の状態で終了するので、

| withoutFinishing: | 動作結果 |
|:------------------:|:------------------|
| true|finishAnimationメソッドを呼び出す必要なく、常にstopAnimation()が呼び出された時点の状態でアニメーションが停止する。|
|false|finishAnimationメソッドが呼び出され、finalPositionパラメータで設定した値によって、UIViewが停止する位置を変更できる。|

という解釈で良いのではないかと思います。（この辺りに関してはもう少し詳細に検証して理解を深めていこうと思います。）

このように、アニメーションの途中でユーザーからのアクションを取得して、
それに応じて動作を変更することができるので、工夫次第で表現の幅がものすごく広がると思うので、個人的にはすごく勉強する甲斐のあるクラスだなと思います。

#終わりに
引き続きUIViewPropertyAnimatorクラスについて勉強し、より深く理解した上で、
また情報を共有できればと思いますので、宜しくお願い致します。

[UIViewPropertyAnimatorの基本まとめ②](http://qiita.com/TsukasaHasegawa/items/cc2d540d90d170de2d1d)
では、ベジエ曲線とUICubicTimingParametersについて、まとめておりますので、
よろしければ、そちらも覗いてみてください。

最後までお付き合い頂き、誠にありがとうございました。