post Image
【iOS】Tinder的アニメーション

最近iOSのAnimation/Transition周りを触ったので、勉強の意味も込めてQiitaに書きたいと思います。

iOSのAnimation/Transitionの実装

今回はTinder的な画面遷移を実装してみたいと思います。

実装後の状態

アニメーション

ソースコードはこちらにあげています。

まず、大きく分けて、画像部分のTransitionの実装と、下の三つのボタンのAnimationがあります。

プロフィール画像部分のTransitionの実装

最初に上のgifでいうメガネのおじさん(ImageView)の遷移時の動きを実装していきます。

必要なプロトコル

・ UIViewControllerAnimatedTransitioning (遷移時の動きを定義する)
・ UIViewControllerTransitioningDelegate (遷移先のcontrollerで適応するdelegate)

概要

まず、UIViewControllerAnimatedTransitioningを実装します。
UIViewControllerAnimatedTransitioning内に遷移時の動きを定義していきます。
transitionContextで遷移先/遷移元のViewControllerが取得できます。

MyTransition.swift

//遷移時のアニメーションを定義
class PresentedAnimater : NSObject, UIViewControllerAnimatedTransitioning {

    let duration = 0.3

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        //animationのdurationを返す
        return duration
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
        guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }

        let container = transitionContext.containerView

        let imageView = (fromVC as! FirstViewController).copyImageView() // <-このcopyImageView()は遷移元のImageViewと同じ大きさ、位置のImageViewを返す
        let destImageViewRect = (toVC as! SecondViewController).copyImageView().frame // <-このcopyImageView()は遷移先のImageViewと同じ大きさ、位置のImageViewを返す
        container.addSubview(toVC.view)
        container.addSubview(imageView)
        toVC.view.alpha = 0.0
        UIView.animate(withDuration: duration, delay: 0.01, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: [], animations: {
            toVC.view.alpha = 1.0
            imageView.frame = destImageViewRect
        }, completion: {_ in
            imageView.isHidden = true
            transitionContext.completeTransition(true)
        })
    }
}

//Dismiss時のアニメーションを定義
class DismissedAnimater : NSObject, UIViewControllerAnimatedTransitioning {

    let duration = 0.3

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        //animationのdurationを返す
        return duration
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else { return }
        guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) else { return }

        let container = transitionContext.containerView

        let imageView = (fromVC as! SecondViewController).copyImageView() 
        let destImageViewRect = (toVC as! FirstViewController).copyImageView().frame
        container.addSubview(toVC.view)
        container.addSubview(imageView)
        toVC.view.alpha = 0.0
        UIView.animate(withDuration: duration, delay: 0.01, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.1, options: [], animations: {
            toVC.view.alpha = 1.0
            imageView.frame = destImageViewRect
        }, completion: {_ in
            transitionContext.completeTransition(true)
        })
    }
}

次に、UIViewControllerTransitioningDelegate部分を書いていきます。

SecondViewController

extension SecondViewController: UIViewControllerTransitioningDelegate {

    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        // 画面遷移時に呼ばれるメソッド 
        return PresentedAnimater()
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        // Dismiss時に呼ばれるメソッド 
        return DismissedAnimater()
    }
}

あとは、SecondViewControllerのviewDidLoadあたりで、

self.transitioningDelegate = self

としてあげれば完成です。

下の三つのボタン部分のAnimationの実装

最後に上のgifでいう三つのボタンが外側からすっと出てくるアニメーションを実装します。
こちらは先ほどのTransitionとは違って遷移後にボタンをアニメーションさせます。

概要

まずはUIButtonを継承したものを実装します。

MovableUIButton

class MovableUIButton: UIButton {

    let repeatCount: Float = 1
    let dumping: CGFloat = 15
    let duration: Double = 1
    let initialVelocity: CGFloat = 15

    let fromBottomKey = "appearFromBottom"
    let fromRightKey = "appearFromBottom"
    let fromLeftKey = "appearFromBottom"

    func appearFromBottom(){
        //今回はボタンを動かすだけなので、"position"
        let animation = CASpringAnimation(keyPath: "position")
        //アニメーションのdurationを定義
        animation.duration = duration
        //アニメーションのrepeatの回数を定義
        animation.repeatCount = repeatCount
        //ハネ具合を定義 (この値が小さいほど伸縮します)
        animation.damping = dumping
        //最初の速度を定義
        animation.initialVelocity = initialVelocity
        //アニメーション開始点を定義
        animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x, y: self.center.y + 100))
        //アニメーション終了点を定義
        animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x, y: self.center.y))

        self.layer.add(animation, forKey: fromBottomKey)
    }

    func appearFromRight(){
        let animation = CASpringAnimation(keyPath: "position")
        animation.duration = duration
        animation.repeatCount = repeatCount
        animation.damping = dumping
        animation.initialVelocity = initialVelocity
        animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x + 100, y: self.center.y))
        animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x, y: self.center.y))

        self.layer.add(animation, forKey: fromRightKey)
    }

    func appearFromLeft(){
        let animation = CASpringAnimation(keyPath: "position")
        animation.duration = duration
        animation.repeatCount = repeatCount
        animation.damping = dumping
        animation.initialVelocity = initialVelocity
        animation.fromValue = NSValue(cgPoint: CGPoint(x: self.center.x - 100, y: self.center.y))
        animation.toValue = NSValue(cgPoint: CGPoint(x: self.center.x, y: self.center.y))

        self.layer.add(animation, forKey: fromLeftKey)
    }
}


次にStoryBoard内でUIButtonのこの部分をMovableUIButtonにセットします。
image

あとは、ControllerのviewDidLayoutSubviewsあたりで、

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        superlikeButton.appearFromBottom()
        nopeButton.appearFromLeft()
        likeButton.appearFromRight()
    }

とすれば、Buttonを動かすことができます。

最後に

こちらにソースコードをあげています。


『 Swift 』Article List