Skip to content

Instantly share code, notes, and snippets.

@raheelahmad
Created May 14, 2019 22:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raheelahmad/ac91e7565b66bd353ee3234349234538 to your computer and use it in GitHub Desktop.
Save raheelahmad/ac91e7565b66bd353ee3234349234538 to your computer and use it in GitHub Desktop.
Custom Presentation basics with UIPresentationController and friends
/// The ring leader
final class ModalTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
func presentationController(
forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController
) -> UIPresentationController? {
return PresentationController(presentedViewController: presented, presenting: presenting)
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PresentationAnimator(presenting: true)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return PresentationAnimator(presenting: false)
}
}
/// Provides actual animation
class PresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let presenting: Bool
init(presenting: Bool) {
self.presenting = presenting
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let key = presenting ? UITransitionContextViewControllerKey.to
: UITransitionContextViewControllerKey.from
let controller = transitionContext.viewController(forKey: key)!
if presenting {
transitionContext.containerView.addSubview(controller.view)
}
let presentedFrame = transitionContext.finalFrame(for: controller)
var dismissedFrame = presentedFrame
dismissedFrame.origin.y -= (presentedFrame.height * (presenting ? 1.2 : 2.0))
let initialFrame = presenting ? dismissedFrame : presentedFrame
let finalFrame = presenting ? presentedFrame : dismissedFrame
let animationDuration = transitionDuration(using: transitionContext)
controller.view.frame = initialFrame
UIView.animate(
withDuration: animationDuration,
delay: 0,
usingSpringWithDamping: 0.9,
initialSpringVelocity: 0,
options: UIView.AnimationOptions.curveEaseIn,
animations: {
controller.view.frame = finalFrame
}) { finished in
transitionContext.completeTransition(finished)
}
}
}
/// Provides orientation and framing. Also side-by-side animation
final class PresentationController: UIPresentationController {
override var frameOfPresentedViewInContainerView: CGRect {
var presentedViewFrame = CGRect.zero
let containerBounds: CGRect = containerView?.bounds ?? .zero
presentedViewFrame.size = size(
forChildContentContainer: presentedViewController,
withParentContainerSize: CGSize(width: containerBounds.size.width - 40, height: containerBounds.size.height)
)
presentedViewFrame.origin.x = 20.0
presentedViewFrame.origin.y = 40.0
return presentedViewFrame
}
override func size(
forChildContentContainer container: UIContentContainer,
withParentContainerSize parentSize: CGSize
) -> CGSize {
return CGSize(width: parentSize.width, height: 60.0)
}
let dimmingView: UIView = UIView()
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
setupDimmingView()
}
func setupDimmingView() {
dimmingView.backgroundColor = UIColor(white: 0, alpha: 0.5)
let tap = UITapGestureRecognizer(target: self, action: #selector(dimmingViewTapped(gesture:)))
dimmingView.addGestureRecognizer(tap)
}
@objc
func dimmingViewTapped(gesture: UIGestureRecognizer) {
if gesture.state == .ended {
presentingViewController.dismiss(animated: true, completion: nil)
}
}
override func presentationTransitionWillBegin() {
dimmingView.frame = self.containerView!.bounds
dimmingView.alpha = 0.0
containerView?.insertSubview(dimmingView, at: 0)
containerView?.layer.cornerRadius = 6.0
if let coordinator = presentedViewController.transitionCoordinator {
coordinator.animate(alongsideTransition: { _ in
self.dimmingView.alpha = 1.0
}, completion: nil)
} else {
dimmingView.alpha = 1.0
}
}
override func dismissalTransitionWillBegin() {
if let coordinator = presentedViewController.transitionCoordinator {
coordinator.animate(alongsideTransition: { _ in
self.dimmingView.alpha = 0.0
}, completion: nil)
} else {
dimmingView.alpha = 0.0
}
}
override func containerViewWillLayoutSubviews() {
dimmingView.frame = containerView!.bounds
presentedView?.frame = frameOfPresentedViewInContainerView
presentedView?.layer.cornerRadius = 6.0
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment