Skip to content

Custom Push Pop Transition

sustainable-git edited this page Nov 12, 2021 · 2 revisions

Custom Push/Pop Transition

  • Navigation Controller는 기본적으로 Push, Pop이 각각 우에서 좌로, 좌에서 우로 Transition이 일어난다

    • 하지만, 이런 Transition도 사용자가 원하는 방식대로 변경할 수 있다
Before After

UINavigationControllerDelegate

  • Custom Transition을 만들기 위해서는 NSObject를 채택한 class가 UINavigationControllerDelegate를 채택해야 한다
  • UINavigationControllerDelegate의 함수는 모두 옵셔널이다
  • navigationController(_, operation, from, to) -> UIViewControllerAnimatedTransitioning?을 구현해야 한다
  • 이 때, operation의 값을 switch하여 분기하는 것이 편리하다

UIViewControllerAnimateTransitioning

  • Custom Transition을 만드려면 NSObject와 UIViewControllerAnimateTransitioning을 상속받는 class를 만들어야 한다

  • 해당 class는 transitionDurationanimateTransition을 구현해야 한다

    • transitionDuration -> TimeInterval

      • transition에 소요되는 시간을 return 한다
    • animateTransition

      • 실제 Transition의 logic이 담긴다

func animateTransition

  • animateTransition(using transitionContext: UIViewControllerContextTransitioning)

    • 해당 함수에서 실제 animation logic이 일어난다
    • transitionContext에서는 Transition Objects, Frames, Behaviors 등을 구할 수 있다
    • 간단하게 fromViewtoView간의 Animation만 적용해 주면 Animation을 구현할 수 있다

Source Code

//
//  ViewController.swift
//  SwiftAppTest5
//
//  Created by shin jae ung on 2021/11/10.
//

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationController?.delegate = self
    }
    
    @IBAction func buttonTouched(_ sender: UIButton) {
        let nvc = NextViewController()
        self.navigationController?.pushViewController(nvc, animated: true)
    }
}


extension ViewController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        switch operation {
        case .push :
            return CustomPushTransition()
        case .pop :
            return CustomPopTransition()
        default:
            return nil
        }
    }
}

class CustomPushTransition: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let from = transitionContext.view(forKey: .from),
              let to = transitionContext.view(forKey: .to)
        else {
            return
        }
        let frame = from.frame
        to.frame = CGRect(x: 0, y: frame.height * 2, width: frame.width, height: frame.height)
        transitionContext.containerView.insertSubview(to, aboveSubview: from)
    
        UIView.animate(
            withDuration: transitionDuration(using: transitionContext),
            delay: 0,
            options: .curveEaseInOut,
            animations: {
                to.frame = from.frame
            }, completion: {_ in
                transitionContext.completeTransition(true)
            }
       )
   }
}

class CustomPopTransition: NSObject, UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.5
    }
    
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let from = transitionContext.view(forKey: .from),
              let to = transitionContext.view(forKey: .to)
        else {
            return
        }
        let frame = from.frame
        to.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height)
        transitionContext.containerView.insertSubview(to, belowSubview: from)

        UIView.animate(
            withDuration: transitionDuration(using: transitionContext),
            delay: 0,
            options: .curveEaseInOut,
            animations: {
                from.frame = CGRect(x: 0, y: frame.height, width: frame.width, height: frame.height)
            }, completion: {_ in
                transitionContext.completeTransition(true)
            }
       )
   }
}
Clone this wiki locally