Skip to content

Instantly share code, notes, and snippets.

@stinger
Last active September 25, 2019 08:58
Show Gist options
  • Save stinger/a3228aafeca78c7cb769bb0a35c6c58f to your computer and use it in GitHub Desktop.
Save stinger/a3228aafeca78c7cb769bb0a35c6c58f to your computer and use it in GitHub Desktop.
Swift 3: Custom Presentation Manager
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