Gargo
Gargo

Reputation: 1377

Custom present transition for 2 nested view controllers with 2 simultaneous animations?

I have 2 nested view controllers with different animations. First one has alpha animation and second one moves from bottom to top with constraint.

My current implementation is just show/hide methods inside first view controller in which I run each animation manually with UIView.animate(withDuration:animations:). But it is not convenient for some reasons (for example I need to call custom methods instead of ones provided by iOS).

Is there a way somehow override present/dissmiss transition to allow to call default present/dismiss methods which will run my animations?

Upvotes: 0

Views: 50

Answers (1)

Gargo
Gargo

Reputation: 1377

Found an appropriate answer here: https://habr.com/ru/articles/424853/

import UIKit

class AnimatorPresent: NSObject, UIViewControllerAnimatedTransitioning {
    let startFrame: CGRect

    init(startFrame: CGRect) {
        self.startFrame = startFrame
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let vcTo = transitionContext.viewController(forKey: .to),
        let snapshot = vcTo.view.snapshotView(afterScreenUpdates: true) else {
            return
        }

        let vContainer = transitionContext.containerView

        vcTo.view.isHidden = true
        vContainer.addSubview(vcTo.view)

        snapshot.frame = self.startFrame
        vContainer.addSubview(snapshot)

        UIView.animate(withDuration: 0.3, animations: {
            snapshot.frame = (transitionContext.finalFrame(for: vcTo))
        }, completion: { success in
            vcTo.view.isHidden = false
            snapshot.removeFromSuperview()
            transitionContext.completeTransition(true)
        })
    }
}
import UIKit

class AnimatorDismiss: NSObject, UIViewControllerAnimatedTransitioning {

    let endFrame: CGRect

    init(endFrame: CGRect) {
        self.endFrame = endFrame
    }

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.3
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let vcTo = transitionContext.viewController(forKey: .to),
        let vcFrom = transitionContext.viewController(forKey: .from),
        let snapshot = vcFrom.view.snapshotView(afterScreenUpdates: true) else {
            return
        }

        let vContainer = transitionContext.containerView
        vContainer.addSubview(vcTo.view)
        vContainer.addSubview(snapshot)

        vcFrom.view.isHidden = true

        UIView.animate(withDuration: 0.3, animations: {
            snapshot.frame = self.endFrame
        }, completion: { success in
            transitionContext.completeTransition(true)
        })
    }
}
extension VCYellow: UIViewControllerTransitioningDelegate {
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return AnimatorPresent(startFrame: self.startFrame)
    }

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return AnimatorDismiss(endFrame: self.startFrame)
    }
}

In short we create classes implementing UIViewControllerAnimatedTransitioning and place all the animation inside them. Then we add UIViewControllerTransitioningDelegate somewhere and assign its object as transitioningDelegate.

Upvotes: 0

Related Questions