Home > Blockchain >  Catch when user drags down modal in iOS Swift
Catch when user drags down modal in iOS Swift

Time:11-30

Im trying to get the same behaviour as Apple has done in it's calendar app for iOS15 (possibly in earlier versions as well) were if you try to dismiss the view you will get presented an Action Sheet asking if you want to discard you changes.

So what I want is to be able to somehow catch the event were the user tries to drag down the view and for example check:

if hasChanges {
// Show Action sheet & stop view from disappearing 
}

This doesn't work as I wan't to in viewWillDisappear as the view just disappears before I get to present the Action Sheet

enter image description here

CodePudding user response:

As described in Apple's docs, you can implement UIAdaptivePresentationControllerDelegate and use presentationControllerDidAttemptToDismiss(_ :) to "intercept" the pull-down action.

Here is a bare-bones example:

class ConfirmDismissViewController: UIViewController, UIAdaptivePresentationControllerDelegate {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .systemYellow
        
        // add a dismiss button
        let b = UIButton(type: .system)
        b.setTitle("Dismiss", for: [])
        b.addTarget(self, action: #selector(btnTapped(_:)), for: .touchUpInside)
        b.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(b)
        NSLayoutConstraint.activate([
            b.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            b.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
        
        // this will trigger a call to presentationControllerDidAttemptToDismiss() on drag-down
        isModalInPresentation = true

        presentationController?.delegate = self
    }

    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
        
        let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
        
        // Only ask if the user wants to save if they attempt to pull to dismiss, not if they tap Cancel.
        alert.addAction(UIAlertAction(title: "Discard Changes", style: .destructive) { _ in
            self.dismiss(animated: true, completion: nil)
        })
        
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        
        present(alert, animated: true, completion: nil)

    }

    @objc func btnTapped(_ sender: Any?) -> Void {
        // dismiss WITHOUT prompt
        dismiss(animated: true, completion: nil)
    }
    
}

When you present this view controller, the user can tap the "Dismiss" button to explicitly dismiss the VC, or, if the user drags-down, you'll be prompted.

  • Related