LoginSignup
20
21

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-11

最近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を動かすことができます。

最後に

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

20
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
21