// // FullScreenTransitionManager.swift // import Foundation import UIKit // MARK: FullScreenPresentationController final class FullScreenPresentationController: UIPresentationController { private lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(onTap)) @objc private func onTap(_ gesture: UITapGestureRecognizer) { presentedViewController.dismiss(animated: true) } } // MARK: UIPresentationController extension FullScreenPresentationController { override func presentationTransitionWillBegin() { guard let containerView = containerView else { return } containerView.addGestureRecognizer(tapGestureRecognizer) } override func presentationTransitionDidEnd(_ completed: Bool) { if !completed { containerView?.removeGestureRecognizer(tapGestureRecognizer) } } override func dismissalTransitionDidEnd(_ completed: Bool) { if completed { containerView?.removeGestureRecognizer(tapGestureRecognizer) } } } // MARK: FullScreenTransitionManager final class FullScreenTransitionManager: NSObject, UIViewControllerTransitioningDelegate { private weak var anchorView: UIView? init(anchorView: UIView) { self.anchorView = anchorView } func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { FullScreenPresentationController(presentedViewController: presented, presenting: presenting) } func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { let anchorFrame = anchorView?.frame ?? CGRect(origin: presented.view.center, size: .zero) return FullScreenAnimationController(animationType: .present, anchorFrame: anchorFrame) } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { let anchorFrame = anchorView?.frame ?? CGRect(origin: dismissed.view.center, size: .zero) return FullScreenAnimationController(animationType: .dismiss, anchorFrame: anchorFrame) } } // MARK: UIViewControllerAnimatedTransitioning final class FullScreenAnimationController: NSObject, UIViewControllerAnimatedTransitioning { enum AnimationType { case present case dismiss } private let animationType: AnimationType private let anchorFrame: CGRect private let animationDuration: TimeInterval private var propertyAnimator: UIViewPropertyAnimator? init(animationType: AnimationType, anchorFrame: CGRect, animationDuration: TimeInterval = 5) { self.animationType = animationType self.anchorFrame = anchorFrame self.animationDuration = animationDuration } func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { animationDuration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { switch animationType { case .present: guard let toViewController = transitionContext.viewController(forKey: .to) else { return transitionContext.completeTransition(false) } transitionContext.containerView.addSubview(toViewController.view) propertyAnimator = presentAnimator(with: transitionContext, animating: toViewController) case .dismiss: guard let fromViewController = transitionContext.viewController(forKey: .from) else { return transitionContext.completeTransition(false) } propertyAnimator = dismissAnimator(with: transitionContext, animating: fromViewController) } } private func presentAnimator(with transitionContext: UIViewControllerContextTransitioning, animating viewController: UIViewController) -> UIViewPropertyAnimator { let finalFrame = transitionContext.finalFrame(for: viewController) viewController.view.frame = anchorFrame viewController.view.layoutIfNeeded() return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.curveEaseInOut], animations: { viewController.view.frame = finalFrame viewController.view.layoutIfNeeded() }, completion: { _ in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) }) } private func dismissAnimator(with transitionContext: UIViewControllerContextTransitioning, animating viewController: UIViewController) -> UIViewPropertyAnimator { return UIViewPropertyAnimator.runningPropertyAnimator(withDuration: transitionDuration(using: transitionContext), delay: 0, options: [.curveEaseInOut], animations: { viewController.view.frame = self.anchorFrame viewController.view.layoutIfNeeded() }, completion: { _ in transitionContext.completeTransition(!transitionContext.transitionWasCancelled) }) } }