iOS10からのAnimationがいい感じ！


こんにちは。現在Standard Cognition（ https://standard.ai )というSFにある会社でSoftware Engineerをやっている原健太です([Twitterアカウント](https://twitter.com/hellokenta_ja))

WWDC16で、iOS10から使えるアニメーションに関する新しい発表がありました。
動的なアニメーションの追加や、一時停止など、柔軟にいろんな事ができるようになっていたので、紹介したいと思います！

## 従来のアニメーション


まず、従来のアニメーションは、こんな感じで書いていたかと思います。

```swift

UIView.animateWithDuration(2.0, delay: 0, options: .CurveEaseInOut, animations: { 

    // animation

}) { _ in

    // completion

}
```

まず、こちらのメソッドも、Swift3からはこんな感じに変わります！

```swift

UIView.animate(withDuration: 2.0, delay: 0, options: [.curveEaseIn, .curveEaseOut], animations: {

}) { _ in

}
```

- メソッド名はシンプルになって、第一引数も外部名が表記するようになっています。
- `UIViewAnimationOptions`が`lowerCamelCase`になっている
- `curveEaseInOut`が無かったのですが、それを実現するには、このようにするんですかね、、？？

ただ、これも、動的にいろんな操作をするには向いていません。

だからといって、`CoreAnimation`も分かりづらかったり、`beginFromCurrentState`というoptionの挙動もわかりづらい、、、

そこで登場したのが、 **UIViewPropertyAnimator**です！こちらのクラスを使うと、アニメーションに対していろんな柔軟な対応ができるようになっています！

## UIViewPropertyAnimator

ざっと、このような感じで使います！

```swift

let timing = UICubicTimingParameters(animationCurve: .easeInOut)
let animator = UIViewPropertyAnimator(duration: 2.0, timingParameters: timing)

animator.addAnimations { 
    // animation
}

animator.startAnimation()

animator.pauseAnimation()
```
なんだか、いろいろできそうな予感がします。

```swift

@IBAction func handlePan(recognizer: UIPanGestureRecognizer) {

   	let translation = recognizer.translation(in: self.view)
  	let translatedCenterY = view.center.y + translation.y
	let progress = translatedCenterY / self.view.bounds.size.height

	animator.fractionComplete = progress
}

```

このように、ユーザのジェスチャに応じてアニメーションの進行を操作することもできます。

### UIViewPropertyAnimator周りのクラス図

![Screen Shot 2016-06-22 at 11.32.31.png](https://qiita-image-store.s3.amazonaws.com/0/54826/b527f24d-5c39-78f0-b671-f285bafb38d6.png "Screen Shot 2016-06-22 at 11.32.31.png")

- UIViewAnimatingとUIViewImplicitlyというプロトコルに準拠しています。
- UITimingCurveProviderというプロトコルに準拠したオブジェクトを受け取って、アニメーションのタイミングを調整します。

## UIViewAnimatingプロトコル
アニメーションのフローを制御するメソッドやプロパティが定義されている。

### メソッド

- `func startAnimation()`
- `func pauseAnimation()`
	- アニメーションの一時停止
	- `AnimatingState`は`active`のまま
- `func stopAnimation(_ withoutFinishing: Bool)`
	- アニメーションを現在のポジションで終了させたい時に利用する
	- 引数にtrueを渡すと直接`inactive`のステートに変化する
	- 引数にfalseを渡すと、`stopped`ステートに変化し、終了させるには`finishAnimation`メソッドを呼ぶ
- `func finishAnimation(at: UIViewAnimatingPosition)`
	- `stopped`ステートの時にこのメソッドを呼んでアニメーションを終了させる
	- `stopped`ステートに遷移する前にこのメソッドを呼ぶとエラーになる。
	- `inactive`ステートに遷移する

### プロパティ

- `var fractionComplete: CGFloat`
	- アニメーションの完了度合いのパーセンテージ
	- 0.0 - 1.0の値をとる
	- この値を操作することにより、アニメーションをインテラクティブに操作できる
- `var isReversed: Bool`
	- このプロパティを操作することによってアニメーションの方向を操作できる
- `var state: UIViewAnimatingState`
	
	Enumで
	- inactive
	- active
	- stopped

  の3つの値を持つ
- `var isRunning: Bool`
	- アニメーションが進行中かどうか


## UIViewImplicitlyAnimatingプロトコル

アニメーションのカスタマイズに関するメソッドが定義されている

### メソッド

- `func addAnimations(() -> Void)`
	- アニメーションを定義する
- `func addAnimations(() -> Void, delayFactor: CGFloat)`
	- 元々のアニメーションに対して、途中から別のアニメーションを開始する場合などに利用する
	- `delayFactor`には0.0 - 1.0の値を入れる
	- 例えば、0.7を入れたら、元々のアニメーションの7割が終了した時点で、追加のアニメーションが開始される
- `func addCompletion((UIViewAnimatingPosition) -> Void)`
	- アニメーションが終了したときの処理を記述する

	UIViewAnimatingPositionはEnumで、
	
	- end
	- start
	- current
	
	 の3つの値がある 

- `func continueAnimation(withTimingParameters: UITimingCurveProvider?, durationFactor: CGFloat)`
	- pauseしているアニメーションに対して、タイミングや、durationを変更して再開したい場合に利用する
	- `durationFactor`の値と、元々の`duration`を掛けた値が最終的な`duration`になる


## UICubicTimingParameters

アニメーションのタイミングを定義するクラス

### イニシャライズ

- `init()`
- `init(animationCurve: UIViewAnimationCurve)`

`UIViewAnimationCurve`には

	- case easeInOut // slow at beginning and end
	- case easeIn // slow at beginning
	- case easeOut // slow at end
	- case linear

の4つが定義されている

- `init(controlPoint1: CGPoint, controlPoint2: CGPoint)`
	- 2つの点を定義することにより、3次ベジェ曲線を定義する

![cubic.png](https://qiita-image-store.s3.amazonaws.com/0/54826/7bb41db2-ce14-74d8-44b7-3c6a0593214a.png "cubic.png")


## UISpringTimingParameters

このクラスのオブジェクトを`UIViewPropertyAnimator`に渡す事によってバネのような動きのアニメーションを実現できる

### イニシャライズ

- `init()`
- `init(dampingRatio: CGFloat)`
	- 減衰比を渡す
	- 0.0 - 1.0の値を取り、小さいほど揺れが大きくなる
- `init(dampingRatio: CGFloat, initialVelocity: CGVector)`
	- 上記のに加え、初期の方向と速度を渡すことができる
- `init(mass: CGFloat, stiffness: CGFloat, damping: CGFloat, initialVelocity: CGVector)`
	- `mass`は質量
	- `stiffness`はバネの硬さ？？
	- `damping`は減衰の強さ？？
	- `initialVelocity`は初期の方向と速度

間違っていたら教えてください。正直、うまい値の渡し方がわかりません笑

![Jun-22-2016 17-08-52.gif](https://qiita-image-store.s3.amazonaws.com/0/54826/8729fb60-4ebd-2263-ccb2-75472322d8de.gif "Jun-22-2016 17-08-52.gif")

このクラスをうまく使うと、このようなアニメーションが実現できるようです！
すごい！！！

## UIViewControllerAnimatedTransitioning

`UIViewPropetyAnimator`を用いたアニメーションは画面遷移にも使えるようです！！

```swift

func animateTransition(_ transitionContext: UIViewControllerContextTransitioning) {

    self.interruptibleAnimator(forTransition: transitionContext)?.startAnimation(
}

func interruptibleAnimator(forTransition transitionContext: UIViewControllerContextTransitioning?) -> UIViewImplicitlyAnimating? {

    let timing = UICubicTimingParameters(animationCurve: .easeInOut)
    let animator = UIViewPropertyAnimator(duration: 2.0, timingParameters: timing)

    animator.addAnimations {
    	// animation
    }

    return animator
}

```

のように、新しく追加された`interruptibleAnimator`メソッドで`UIViewPropertyAnimator`のオブジェクトを返して、従来使っていた`animateTransition`メソッドでアニメーションを開始してあげる、という流れです。

## まとめ

- なんだか気持ち良いアニメーションが作りたくなった
- iOS10からの対応なので、半年後とか1年後くらいまで使えない！
- [セッション動画](https://developer.apple.com/videos/play/wwdc2016/216/)はこちらです！！