Yumico’s blog

iOS開発ブログ

UIViewを曲線上にアニメーションさせる方法

今回は半径分だけアニメーションさせて見ます。 f:id:Yumico:20170116191902g:plain

まずは全コードを。。
開発環境:Xcode8 + swift3
スタートボタンのイベントはIBかストーリーボードで設定して繋げてください。

import UIKit

class CurveAnimationViewController: UIViewController {

    let animateView: UIView = UIView()
    let viewSize: CGFloat = 100.0
    let curcleRadius: CGFloat = 150.0
    let duration: TimeInterval = 0.5
    var isDownView: Bool = false
    
    override func viewDidLoad() {
        super.viewDidLoad()
        animateView.frame = CGRect(x: (UIScreen.main.bounds.width / 2) - (viewSize / 2), y: UIScreen.main.bounds.height / 2 - viewSize / 2, width: viewSize, height: viewSize)
        animateView.layer.cornerRadius = viewSize / 2.0
        animateView.backgroundColor = UIColor.orange
        self.view.addSubview(animateView)      
    }
    
    @IBAction func tappedStartButton(_ sender: UIButton) {
        isDownView = !isDownView
        if isDownView {
            self.downAnimation()
        }else{
            self.upAnimation()
        }
    }
}


//MARK: - animation
extension CurveAnimationViewController {
    
    func downAnimation() {
        animateView.layer.removeAnimation(forKey: "end")
        let animation: CAKeyframeAnimation = self.animation()
        let path: CGMutablePath = CGMutablePath()      
        let startPoint: CGPoint = CGPoint(x: animateView.frame.minX + (animateView.frame.width / 2.0), y: animateView.frame.minY + (animateView.frame.height / 2.0))
        let midPoint: CGPoint = CGPoint(x: startPoint.x + curcleRadius, y: startPoint.y + curcleRadius / 2.0)
        let endPoint: CGPoint = CGPoint(x: startPoint.x, y: startPoint.y + curcleRadius)
        
        path.move(to: startPoint)
        path.addQuadCurve(to: endPoint, control: midPoint)
        animation.path = path
        animateView.layer.add(animation, forKey: "start")
    }
    
    func upAnimation() {
        animateView.layer.removeAnimation(forKey: "start")
        let animation: CAKeyframeAnimation = self.animation()
        let path: CGMutablePath = CGMutablePath()
        let startPoint: CGPoint = CGPoint(x: animateView.frame.minX + (animateView.frame.width / 2.0), y: animateView.frame.minY + (animateView.frame.height / 2.0) + curcleRadius)
        let midPoint: CGPoint = CGPoint(x: startPoint.x + curcleRadius, y: startPoint.y - curcleRadius / 2.0)
        let endPoint: CGPoint = CGPoint(x: animateView.frame.minX + (animateView.frame.width / 2.0), y: startPoint.y - curcleRadius)
        
        path.move(to: startPoint)
        path.addQuadCurve(to: endPoint, control: midPoint)
        animation.path = path
        animateView.layer.add(animation, forKey: "end")
    }

    func animation() -> CAKeyframeAnimation {
        let animation: CAKeyframeAnimation = CAKeyframeAnimation(keyPath: "position")
        animation.fillMode = kCAFillModeForwards
        animation.isRemovedOnCompletion = false
        animation.duration = duration
        return animation
    }
}

詳しく説明をしていきます。
今回はUIViewをアニメーションさせるためにCAKeyframeAnimationを使用しています。
func animation() -> CAKeyframeAnimation内で設定している内容は下記の通りです。

CAKeyframeAnimation 説明
fillMode アニメーションの再生中もしくは再生後のスタイル。 プロパティについてはこちらのサイトがわかりやすくまとめていました。【iOS Swift アニメーション入門 #4】Core AnimationのfillModeプロパティ | Swift,Objective-Cプログラミング ~ iOS ~
isRemovedOnCompletion アニメーション完了時に、ターゲットのレイヤーからアニメーションを削除するかどうかを設定。デフォルトはYes
duration アニメーションの長さ。

このCAKeyframeAnimationに作成したパスを入れて、動かしたいUIViewのレイヤーに加えてあげればアニメーションが開始されます。
では、パスをどのように設定しているか見ていきしょう。

let path: CGMutablePath = CGMutablePath()
//出発地点 > 動かしたいViewのセンターを計算しています。
let startPoint: CGPoint = CGPoint(x: animateView.frame.minX + (animateView.frame.width / 2.0), y: animateView.frame.minY + (animateView.frame.height / 2.0) + curcleRadius)
//中間地点 > 出発地点と最終地点の真ん中の位置を計算します。
let midPoint: CGPoint = CGPoint(x: startPoint.x + curcleRadius, y: startPoint.y - curcleRadius / 2.0)
//最終地点 > 最終地点のポジション。
let endPoint: CGPoint = CGPoint(x: animateView.frame.minX + (animateView.frame.width / 2.0), y: startPoint.y - curcleRadius)
        
//出発地点のポイントを設定します。
path.move(to: startPoint)
//最終地点と中間地点を設定します。
path.addQuadCurve(to: endPoint, control: midPoint)
//先ほどのCAKeyframeAnimationにパスを設定。
animation.path = path
//UIViewにCAKeyframeAnimationを追加。
animateView.layer.add(animation, forKey: "start")

pathの数値を変えるなどして試して見てください。
特に難しい計算を必要とせずに、どこに向かってアニメーションさせたいかのポイントを入れるだけなので、とても簡単に実装できます。