iosswiftcustom-transition

Custom push transition isn't working whereas the same custom present transition is


I have followed this tutorial and watched the WWDC video on this subject but I couldn't find my answer.

I have almost the same transition in my code. It is working pretty well when I am doing it as a presented view, but not as a pushed view.

It is supposed to animate a snapshot of the pushed view from a CGRect to the full screen and vice versa when popped.

Here is the code of my UIViewControllerAnimatedTransitioning class:

class ZoomingTransitionController: NSObject, UIViewControllerAnimatedTransitioning {

    let originFrame: CGRect
    let isDismissing: Bool

    init(originFrame: CGRect, isDismissing: Bool) {
        self.originFrame = originFrame
        self.isDismissing = isDismissing
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return Constant.Animation.VeryShort
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let containerView = transitionContext.containerView

        guard let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from),
            let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) else {
                return
        }

        let finalFrame = transitionContext.finalFrame(for: toVC)

        toVC.view.frame = finalFrame

        let snapshot = self.isDismissing ? fromVC.view.snapshotView(afterScreenUpdates: true) : toVC.view.snapshotView(afterScreenUpdates: true)
        snapshot?.frame = self.isDismissing ? finalFrame : self.originFrame
        snapshot?.layer.cornerRadius = Constant.FakeButton.CornerRadius
        snapshot?.layer.masksToBounds = true

        containerView.addSubview(toVC.view)
        containerView.addSubview(snapshot!)

        if self.isDismissing {
            fromVC.view.isHidden = true
        } else {
            toVC.view.isHidden = true
        }

        let duration = transitionDuration(using: transitionContext)

        UIView.animate(withDuration: duration,
                       animations: {
            snapshot?.frame = self.isDismissing ? self.originFrame : finalFrame
            },
                       completion: { _ in
                        if self.isDismissing {
                            fromVC.view.isHidden = false
                        } else {
                            toVC.view.isHidden = false
                        }

                        snapshot?.removeFromSuperview()
                        transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        })
    }
}

Then, I tried to show a new View Controller using 2 ways: by presenting it and by pushing it.

My FromViewController is subclassing both UINavigationControllerDelegate and UIViewControllerTransitioningDelegate.

FromViewController class presenting the ToViewController (which works fine):

func buttonAction(_ sender: AnyObject) {
    self.tappedButtonFrame = sender.frame

    let toVC = self.storyboard!.instantiateViewController(withIdentifier: "ToViewController")
    toVC.transitioningDelegate = self

    self.present(toVC, animated: true, completion: nil)
}

func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    let transitionController = ZoomingTransitionController(originFrame: self.tappedButtonFrame, isDismissing: false)

    return transitionController
}

func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    let transitionController = ZoomingTransitionController(originFrame: self.tappedButtonFrame, isDismissing: true)

    return transitionController
}

FromViewController class pushing the ToViewController (which doesn't work):

func buttonAction(_ sender: AnyObject) {
    self.tappedButtonFrame = sender.frame

    let toVC = self.storyboard!.instantiateViewController(withIdentifier: "ToViewController")

    self.navigationController?.pushViewController(toVC, animated: true)
}

func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
    switch operation {
    case .push:
        return ZoomingTransitionController(originFrame: self.tappedButtonFrame, isDismissing: false)

    case .pop:
        return ZoomingTransitionController(originFrame: self.tappedButtonFrame, isDismissing: true)

    default:
        return nil
    }
}

When pushing, the delegate method is called and the ZoomingTransitionController performs its code fine (going in animateTransition until the end without notable issue). But on the screen, the snapshot view isn't display at any moment. The ToVC appears after the transition duration, but without anything else meanwhile.

I am running out of idea on how to debug this... Do you have any idea?

Thanks!!


Solution

  • I found my answer by replacing the snapshot element (which was causing the problem) by a CGAffineTransform of the toVC.

    Code is almost the same than here.