Surprise & Make Sense

Hara Kentaです。

iOS10からのAnimationがいい感じ!

※コチラの記事は、Qiitaのこちらの記事と同じ内容です。

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

従来のアニメーション

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

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

    // animation

}) { _ in

    // completion

}

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

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

}) { _ in

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

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

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

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

UIViewPropertyAnimator

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

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

animator.addAnimations { 
    // animation
}

animator.startAnimation()

animator.pauseAnimation()

なんだか、いろいろできそうな予感がします。

@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

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

UIViewAnimatingプロトコル

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

メソッド

  • func startAnimation()
  • func pauseAnimation()
    • アニメーションの一時停止
    • AnimatingStateactiveのまま
  • 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

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

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

UIViewControllerAnimatedTransitioning

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

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年後くらいまで使えない!
  • セッション動画はこちらです!!