search
LoginSignup
134
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Organization

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

概要

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にあげてあります。

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
What you can do with signing up
134
Help us understand the problem. What are the problem?