UIKit - オリジナル画面遷移を作ってみる(1)

とっても今更だが、オリジナルの画面遷移ができる方法をしっておきたかったので、
テストがてら簡単なものをつくって自分なりにまとめる。

今回はモーダルで開くときの画面遷移をオリジナルにしたが、
同様のやり方でUINavigationControllerのPush/PopやUITabControllerの切り替えにも対応できる。

必要なもの

・MainViewController(遷移元のViewController)
・ModalViewController(遷移先のViewController)
・AnimationController(遷移のアニメーション処理を受け持つクラス。UIViewControllerAnimatedTransitioningを実装)

遷移元と遷移先のViewControllerは当たり前として、
UIViewControllerAnimatedTransitioningプロトコルを実装したクラス一つ作り、 遷移元にUIViewControllerTransitioningDelegateを実測してやれば、
簡単にカスタム遷移を実現することができます。UIKit便利ですね。

アニメーションコントローラーの作成

遷移アニメーション処理を受け持つクラスを作成します。
ここでは、遷移元ビューコントローラーでpresentationViewControllerした時に、
登場時(presenting==true)のときには、右から新たなビューコントローラーが出てくるように、
退去時(presenting==false)のときには、右に表示ビューコントローラーが消えていくようにアニメーションさせています。

class SlideAnimationController: NSObject, UIViewControllerAnimatedTransitioning{
    
    let presenting: Bool
    
    init(presenting: Bool) {
        self.presenting = presenting
        super.init()
    }
    
    // アニメーションにかける時間
    func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
        return NSTimeInterval(0.6)
    }
    
    // アニメーション
    func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
        
        guard let containerView = transitionContext.containerView(),
            fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
            toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
            else {
            return
        }
        
        let fromView = fromVC.view
        let toView = toVC.view
        
        let inframe = transitionContext.initialFrameForViewController(fromVC)
        let outframe = CGRectOffset(inframe, CGRectGetWidth(inframe), 0)
        
        if presenting {
            
            containerView.addSubview(toView)

            toView.frame = outframe
            
            UIView.animateWithDuration(self.transitionDuration(transitionContext), animations: { 
                toView.frame = inframe
                }, completion: { (finished) in
                    transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
            })
        }
        else {
            
            containerView.insertSubview(toView, belowSubview: fromView)
            
            toView.frame = inframe
            
            UIView.animateWithDuration(self.transitionDuration(transitionContext), animations: { 
                fromView.frame = outframe
                }, completion: { (finished) in
                    transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
            })
        }
    }
}

遷移元ビューコントローラーにUIViewControllerTransitioningDelegateを実装します

// MainViewController

func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return SlideAnimationController(presenting: true)
    }
    
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return SlideAnimationController(presenting: false)
}

あとは遷移するだけ

let modalVC = ModalViewController()
modalVC.transitioningDelegate = self
modalVC.modalPresentationStyle = .FullScreen // .FullScreen or .Customを設定する
presentViewController(modalVC, animated: true, completion: nil)

案外簡単にできたので、次はユーザー操作に合わせて画面遷移させてみたいと思います。 コードはこちら↓

github.com