I’m trying to create a custom, short menu that slides up from the bottom of the screen and stays at the bottom (like the iOS share sheet). I’m having a hard time trying to figure out how to do it. I tried presenting a view controller as a modal and setting the preferred content size, but it still presents it as full screen. How can I present a short, modal-like overlay?
            Asked
            
        
        
            Active
            
        
            Viewed 2,523 times
        
    1 Answers
6
            You could use a UIPresentationController and a UIViewControllerTransitioningDelegate.
As a starting point here a few lines of code:
UIViewControllerTransitioningDelegate
class OverlayTransitioningDelegate: NSObject, UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        return OverlayPresentationController(presentedViewController:presented, presenting:presenting)
    }
}
UIPresentationController
class OverlayPresentationController: UIPresentationController {
    private let dimmedBackgroundView = UIView()
    private let height: CGFloat = 200.0
    override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(backgroundTapped))
        self.dimmedBackgroundView.addGestureRecognizer(tapGestureRecognizer)
    }
    override var frameOfPresentedViewInContainerView: CGRect {
        var frame =  CGRect.zero
        if let containerBounds = containerView?.bounds {
            frame = CGRect(x: 0,
                           y: containerBounds.height - height,
                           width: containerBounds.width,
                           height: height)
        }
        return frame
    }
    override func presentationTransitionWillBegin() {
        if let containerView = self.containerView, let coordinator = presentingViewController.transitionCoordinator {
            containerView.addSubview(self.dimmedBackgroundView)
            self.dimmedBackgroundView.backgroundColor = .black
            self.dimmedBackgroundView.frame = containerView.bounds
            self.dimmedBackgroundView.alpha = 0
            coordinator.animate(alongsideTransition: { _ in
                self.dimmedBackgroundView.alpha = 0.5
            }, completion: nil)
        }
    }
    override func dismissalTransitionDidEnd(_ completed: Bool) {
        self.dimmedBackgroundView.removeFromSuperview()
    }
    @objc private func backgroundTapped() {
       self.presentedViewController.dismiss(animated: true, completion: nil)
    }
}
How to call it
let overlayTransitioningDelegate = OverlayTransitioningDelegate()
@IBAction func onOpenModalOverlay(_ sender: Any) {
    let overlayVC = OverlayViewController()
    overlayVC.transitioningDelegate  = self.overlayTransitioningDelegate
    overlayVC.modalPresentationStyle = .custom
    self.present(overlayVC, animated: true, completion: nil)
}
Demo
The OverlayViewController is a normal ViewController. Here I used an ugly green background color to make it easier to recognize the overlay.
 
    
    
        Stephan Schlecht
        
- 26,556
- 1
- 33
- 47
 
    