Last active
September 25, 2019 08:58
-
-
Save stinger/a3228aafeca78c7cb769bb0a35c6c58f to your computer and use it in GitHub Desktop.
Swift 3: Custom Presentation Manager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
final class SDPresentationManager: NSObject, UIViewControllerTransitioningDelegate { | |
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? { | |
return SDModalPresentationController(presentedViewController: presented, presenting: source) | |
} | |
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { | |
return SDModalTransitionPresentationAnimator() | |
} | |
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { | |
return SDModalTransitionDismissAnimator() | |
} | |
} | |
final class SDModalPresentationController: UIPresentationController { | |
var dimmingView: UIView! | |
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) { | |
super.init(presentedViewController: presentedViewController, presenting: presentingViewController) | |
self.setupDimmingView() | |
} | |
func setupDimmingView() { | |
self.dimmingView = UIView(frame: presentingViewController.view.bounds) | |
let visualEffectView = UIVisualEffectView() | |
visualEffectView.effect = UIBlurEffect(style: .dark) | |
visualEffectView.frame = dimmingView.bounds | |
visualEffectView.autoresizingMask = [.flexibleHeight, .flexibleWidth] | |
self.dimmingView.addSubview(visualEffectView) | |
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(DBModalPresentationController.dimmingViewTapped(_:))) | |
self.dimmingView.addGestureRecognizer(tapRecognizer) | |
} | |
func dimmingViewTapped(_ tapRecognizer: UITapGestureRecognizer) { | |
presentingViewController.dismiss(animated: true, completion: nil) | |
} | |
override func presentationTransitionWillBegin() { | |
let containerView = self.containerView | |
let presentedViewController = self.presentedViewController | |
self.dimmingView.frame = self.frameOfPresentedViewInContainerView | |
self.dimmingView.alpha = 0.0 | |
containerView!.insertSubview(self.dimmingView, at: 0) | |
presentedViewController.transitionCoordinator?.animate(alongsideTransition: { [weak self] (coordinatorContext) -> Void in | |
self?.dimmingView.alpha = 1.0 | |
}, completion: nil) | |
} | |
override func dismissalTransitionWillBegin() { | |
presentedViewController.transitionCoordinator?.animate(alongsideTransition: { [weak self] (coordinatorContext) -> Void in | |
self?.dimmingView.alpha = 0.0 | |
}, completion: nil) | |
} | |
override func containerViewWillLayoutSubviews() { | |
self.dimmingView.frame = containerView!.bounds | |
presentedView!.frame = self.frameOfPresentedViewInContainerView | |
} | |
override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize { | |
return CGSize(width: parentSize.width - 40, height: parentSize.height - 80) | |
} | |
override var frameOfPresentedViewInContainerView: CGRect { | |
let containerBounds = self.containerView?.bounds ?? CGRect.zero | |
let contentContainer = self.presentedViewController | |
var presentedViewFrame = CGRect(origin: CGPoint.zero, size: self.size(forChildContentContainer: contentContainer, withParentContainerSize: containerBounds.size)) | |
presentedViewFrame.origin.x = (containerBounds.size.width - presentedViewFrame.size.width) / 2 | |
presentedViewFrame.origin.y = (containerBounds.size.height - presentedViewFrame.size.height) / 2 | |
return presentedViewFrame | |
} | |
} | |
final class SDModalTransitionPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning { | |
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | |
return 0.3 | |
} | |
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | |
let toViewController = transitionContext.viewController(forKey: .to) as UIViewController! | |
let containerView = transitionContext.containerView | |
let animationDuration = self.transitionDuration(using: transitionContext) | |
toViewController?.view.transform = CGAffineTransform(translationX: 0, y: containerView.frame.size.height) | |
containerView.addSubview((toViewController?.view)!) | |
UIView.animate(withDuration: animationDuration, animations: { () -> Void in | |
toViewController?.view.transform = CGAffineTransform.identity | |
}, completion: { (finished) -> Void in | |
transitionContext.completeTransition(finished) | |
}) | |
} | |
} | |
final class SDModalTransitionDismissAnimator : NSObject, UIViewControllerAnimatedTransitioning { | |
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { | |
return 0.3 | |
} | |
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { | |
let fromViewController = transitionContext.viewController(forKey: .from)! | |
let containerView = transitionContext.containerView | |
let animationDuration = self.transitionDuration(using: transitionContext) | |
UIView.animate(withDuration: animationDuration, animations: { () -> Void in | |
fromViewController.view.alpha = 0.0 | |
fromViewController.view.transform = CGAffineTransform(translationX: 0, y: containerView.frame.size.height) | |
}) { (finished) -> Void in | |
transitionContext.completeTransition(!transitionContext.transitionWasCancelled) | |
} | |
} | |
} | |
// usage (sender is the presenting view controller. sdPresentationManager is a member of sender inilialzed as let sdPresentationManager = SDPresentationManager()): | |
// destinationVC.transitioningDelegate = sender.sdPresentationManager | |
// destination.modalPresentationStyle = .custom | |
// sender.present(receiver, animated: true, completion: nil) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment