[iOS10] UIViewPropertyAnimatorによるアニメーション

2016.11.24

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

1 はじめに

iOS 10 で新しく追加されたUIViewPropertyAnimatorによって、アニメーションロジックを簡単に記述することが可能になりました。

今回は、この記述要領について、覚書として纏めてみました。

なお、UIViewPropertyAnimatorについては、既に、ここDevelopers.IOで紹介されていますので、クラス構造や、全機能の体系については、下記を御覧ください。
[iOS 10][UIKit] 新しく加わった動的なアニメーション実装の方法とその周辺解説

2 Initializing a Property Animator

(1) 最も単純なアニメーション

ビューの中心位置を変更するアニメーションです。

let animator = UIViewPropertyAnimator(duration: 2.0, 
                                         curve: .linear){
    kao.center = endPoint
}
animator.startAnimation()

002

上記の例では、パラメータにduration:(アニメーションの持続時間)及びcurve:(アニメーションカーブ)が指定されていますが、cueve:には、次の4つ(UIViewAnimationCurve列挙型)が指定可能です。

  • .liner (直線)
  • .easeIn (初めゆっくり)
  • .easeInOut (初めと終わりがゆっくり)
  • .easeOut (終わりゆっくり)

(2) 3次ベジェ曲線

先の例では、アニメーションカーブをUIViewAnimationCurveで指定しましたが、2点指定によるベジェ曲線で指定することも可能です。

let animator = UIViewPropertyAnimator(duration: 2.0,
                                    controlPoint1: CGPoint(x: 0.1,y: 0.5),
                                    controlPoint2: CGPoint(x: 0.5, y:0.2),
                                       animations: {
    kao.center = endPoint
})
animator.startAnimation()

004

(3) スプリングエフェクト

dampingRatioで0〜1の減衰値を指定することで、スプリングエフェクトを定義できます。

let animator = UIViewPropertyAnimator(duration: 2.0, 
                                  dampingRatio: 0.3, 
                                    animations: {
    kao.center = endPoint
})
animator.startAnimation()

005

3 UIViewAnimating

(1) UIViewAnimatingState

UIViewPropertyAnimatorの継承元である、UIViewAnimatingでは、startAnimation()pauseAnimation()stopAnimation(_:)などのメソッドによってステータスを変更します。

ステータスには、以下の3つがあります。 * Inactive (非アクティブ) * Active(アクティブ) * Stopped(停止)

003
API Reference UIViewAnimatingより

(2) fractionComplete

fractionCompleteは、アニメーションの進行具合を0〜1で表現しています。 そして、これは、セッターとして利用することも可能です。

下記の例は、スライダーによって、アニメーションの進行具合を変更している様子です。アニメーションを開始していないので、ステータスは非アクティブのままです。

var animator:UIViewPropertyAnimator?
animator = UIViewPropertyAnimator(duration: 2.0, curve: .linear){
    kao.center = endPoint
}
@IBAction func sliderValueChanged(_ sender: UISlider) {
    self.animator?.fractionComplete = CGFloat(sender.value)
}

009

(3) isReversed

isReversedというプロパティをセットすることで、アニメーションはリバースします。 下記の例は、アニメーションが開始され、アクティブ状態のものをリバースしています。

var animator:UIViewPropertyAnimator?
animator = UIViewPropertyAnimator(duration: 2.0, curve: .linear){
    kao.center = endPoint
}
animator?.startAnimation()
@IBAction func tapReverseButton(_ sender: Any) {
    animator?.isReversed = (animator?.isReversed)! ? false : true
}

010

4 Modifying Animations

(1) 追加

UIViewPropertyAnimatorは、UIViewImplicitlyAnimatingプロトコルを採用しており、簡単にアニメーションを追加することが可能です。

下記は、addAnimations(_:)による初期化時の追加です。

let animator = UIViewPropertyAnimator(duration: 2.0, curve: .linear){
    kao.center = endPoint
}
animator.addAnimations{
    kao.alpha = 0.0
}
animator.startAnimation()

006

(2) 遅延指定

addAnimations(_:delayFactor:)では、追加するアニメーションの開始時期を遅らせることも可能です。 次の例は、alpha値の変化開始を全体の90%のアニメーションが進行した以降に指定しています。

let animator = UIViewPropertyAnimator(duration: 2.0, curve: .linear){
    kao.center = endPoint
}
animator.addAnimations({
    kao.alpha = 0.0
}, delayFactor: 0.9)
animator.startAnimation()

007

(3) 終了後

addCompletion(_:)は、アニメーション終了後の変化を指定します。

let animator = UIViewPropertyAnimator(duration: 2.0, curve: .linear){
    kao.center = endPoint
}
animator.addCompletion {_ in 
    kao.bounds = CGRect(x: 0, y: 0, width: 70, height: 70)
}
animator.startAnimation()

008

アニメーションの追加は、初期化時に限らず、実行中のアニメーションにも可能です。 この場合、残り時間をアニメーションの継続時間として直ちに実行されます。

5 UITimingCurveProvider

UIViewPropertyAnimatorのタイミング指定で使用されるUITimingCurveProviderとしてUISpringTimingParametersが利用可能です。

これを使用するとバネのようなアニメーションが簡単に作成できます。

@IBOutlet weak var kao: UIImageView!
var animator:UIViewPropertyAnimator?
var startPosition:CGPoint?
override func viewDidLoad() {
    super.viewDidLoad()

    self.startPosition = self.kao.center
    self.kao.isUserInteractionEnabled = true
    self.kao.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.dragKao)))
}
func dragKao(gesture: UIPanGestureRecognizer) {
    // ImageViewの相対位置    
    let translation = gesture.translation(in: self.view)
    // View内での絶対位置
    var position: CGPoint = CGPoint(x: translation.x + self.startPosition!.x, y: translation.y + self.startPosition!.y)

    switch gesture.state {
    case .began:
        if animator != nil && animator!.isRunning {
            animator!.stopAnimation(false)
        }
        kao.center = position
    case .changed:
        kao.center = position
    case .ended:
        let v = gesture.velocity(in: kao)
        let velocity = CGVector(dx: v.x / 500, dy: v.y / 500)
        let springParameters = UISpringTimingParameters(mass: 30, stiffness: 800, damping: 100, initialVelocity: velocity)
        animator = UIViewPropertyAnimator(duration: 0.0, timingParameters: springParameters)
        animator!.addAnimations({
            self.kao.center = self.view.center
        })
        animator!.startAnimation()
    default: break
}

011

6 最後に

UIViewPropertyAnimatorを使用すると、アニメーションの組み合わせが、非常にシンプルに記述できるように感じました。 なんとかUITimingCurveProviderを使いこなして、自由にアニメーションを作れるようになりたいと思います。

7 参考資料


iOS10からのAnimationがいい感じ!
Designing Animations with UIViewPropertyAnimator in iOS 10 and Swift 3