Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

UIViewControllerAnimatedTransitioningを使って画面遷移アニメーションを作る

More than 5 years have passed since last update.

概要

UIViewControllerAnimatedTransitioningを使うと簡単に画面遷移アニメーションを作れます。
まず、UIViewControllerAnimatedTransitioningプロトコルを適用したクラスを作ります(これが画面遷移の実装内容となります)。
ポイントだけ以下に示します。

Animator.swift
class Animator: NSObject, UIViewControllerAnimatedTransitioning {

    func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
        // アニメーションの時間(duration)を返します。
        return 1.0
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        // ここでアニメーションの具体的な内容を書きます。 
        let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
        let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)

        UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0, options: .CurveEaseInOut, animations: { () -> Void in
             // アニメーションの具体的な内容
        }) { (finished) -> Void in
            // 完了後の処理
            transitionContext.completeTransition(true)
        }       
    }
}

そして、 遷移先 のViewControllerでUIViewControllerTransitioningDelegateを適用し、以下のように書くと自作の画面遷移ができるようになります(UINavigationControllerを使わない場合)。

SecondViewController.swift
class SecondViewController: UIViewController, UIViewControllerTransitioningDelegate {
    let kAnimator = Animator()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.transitioningDelegate = self // delegateにselfを設定
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func buttonTapped(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)
    }


    // MARK: - UIViewControllerTransitioningDelegate

    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {    
        // この画面に遷移してくるときに呼ばれるメソッド    
        return kAnimator
    }

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {    
        // この画面から遷移元に戻るときに呼ばれるメソッド     
        return kAnimator
    }
}

UINavigationControllerを使うときは 遷移元 のViewControllerに以下のように書くと遷移できます。

ViewController.swift
class ViewController: UIViewController {
    let kAnimationController = FadeAnimationController()
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {        
        return kAnimationController
    }
}

上のテンプレートに沿って、実際にpush(show)のような簡単な画面遷移を実装してみます。

Animator.swift
class Animator: NSObject, UIViewControllerAnimatedTransitioning {
    let kMovedDistance: CGFloat = 70.0 // 遷移元のviewのずれる分の距離
    let kDuration = 0.3
    var presenting = false // 遷移するときtrue(戻るときfalse)

    func transitionDuration(transitionContext: UIViewControllerContextTransitioning) -> NSTimeInterval {
        return kDuration
    }

    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
        let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)

        // 遷移するときと戻るときとで処理を変える
        if presenting {
            presentTransition(transitionContext, toView: toVC!.view, fromView: fromVC!.view)
        } else {
            dismissTransition(transitionContext, toView: toVC!.view, fromView: fromVC!.view)
        }
    }

    // 遷移するときのアニメーション
    func presentTransition(transitionContext: UIViewControllerContextTransitioning, toView: UIView, fromView: UIView) {
        let containerView = transitionContext.containerView()
        containerView.insertSubview(toView, aboveSubview: fromView) // toViewの下にfromView

        // 遷移先のviewを画面の右側に移動させておく。
        toView.frame = CGRectOffset(toView.frame, containerView.frame.size.width, 0)

        UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0.05, options: .CurveEaseInOut, animations: { () -> Void in
            // 遷移元のviewを少し左へずらし、alpha値を下げて少し暗くする。
            fromView.frame = CGRectOffset(fromView.frame, -self.kMovedDistance, 0)
            fromView.alpha = 0.7

            // 遷移先のviewを画面全体にはまるように移動させる。
            toView.frame = containerView.frame
        }) { (finished) -> Void in
            fromView.frame = CGRectOffset(fromView.frame, self.kMovedDistance, 0) // 元の位置に戻す
            transitionContext.completeTransition(true)
        }
    }

    // 戻るときのアニメーション
    func dismissTransition(transitionContext: UIViewControllerContextTransitioning, toView: UIView, fromView: UIView) {
        let containerView = transitionContext.containerView()
        containerView.insertSubview(toView, belowSubview: fromView) // fromViewの下にtoView

        // 上と逆のことをする。
        toView.frame = CGRectOffset(toView.frame, -kMovedDistance, 0)

        UIView.animateWithDuration(transitionDuration(transitionContext), delay: 0, options: .CurveEaseInOut, animations: { () -> Void in
            fromView.frame = CGRectOffset(fromView.frame, containerView.frame.size.width, 0)
            toView.frame = CGRectOffset(toView.frame, self.kMovedDistance, 0)
            toView.alpha = 1.0
        }) { (finished) -> Void in
            transitionContext.completeTransition(true)
        }
    }
}

この例では、フラグを使って遷移するときと戻るときとでアニメーションを変化させています。
そのため、遷移先のviewでは以下のように、フラグを操作します。

SecondViewController.swfit
...

// MARK: - UIViewControllerTransitioningDelegate

    func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        kAnimator.presenting = true // 遷移してくるときにtrueにする

        return kAnimator
    }

    func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        kAnimator.presenting = false // 遷移元に戻るときにfalseにする

        return kAnimator
    }
}

このようにすると、以下のようにシンプルな画面遷移になります。

Preview

上で作成したものはGitHubにあげてあります。

kitoko552
Software Engineer at CyberAgent, Inc. Flutter/Dart, iOS/Swift, Node.js, Kubernetes
https://note.com/kitoko552
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away