Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS 13 Modals - Calling swipe dismissal programmatically

I'd like to detect a modal dismissal in the view controller that's presenting the modal.

This method works amazing for detecting the new iOS 13 swipe dismissal on the new card modals:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "MyIdentifier" {
        segue.destination.presentationController?.delegate = self
    }
}

extension MyController: UIAdaptivePresentationControllerDelegate {    
    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        //clean up UI (de-selecting stuff) once modal has been dismissed
    }
}

However, presentationControllerDidDismiss is NOT called if the modal dismisses itself programmatically through an action:

@IBAction func btnDismissTap(_ sender: Any) {
    self.dismiss(animated: true, completion: nil)
}

Is this a bug or is there a way I can programmatically call whatever the "swipe" dismiss is so I can detect all dismissals the same way? Currently I'm writing extra "dismiss" delegate methods into my modals as a work around and it seems unnecessary.

like image 995
William T. Avatar asked Sep 15 '19 07:09

William T.


3 Answers

However, presentationControllerDidDismiss is NOT called if the modal dismisses itself programmatically through an action

 self.dismiss(animated: true, completion: nil)

It doesn’t need to be called, because you dismissed the modal yourself, in code. You cannot not know that the modal was dismissed. You don’t need to receive a dismissal signal, because you gave the dismissal signal in the first place.

You typically don’t get a delegate method call reporting something your own code did. Delegate methods report user actions. It would be crazy if everything you yourself did in code came back as a delegate method call.

like image 122
matt Avatar answered Nov 15 '22 20:11

matt


Mojtaba Hosseini, answer is something I was looking for.

Currently, I need to write a delegate function to let the presenting view know that the user dismissed the modal PLUS do the presentationControllerDidDismiss handler for swipe dismissals:

@IBAction func btnDismissTap(_ sender: Any) {
    self.dismiss(animated: true, completion: {
        self.delegate?.myModalViewDidDismiss()
    })
}

I wanted to handle both of these the same way and Mojtaba's answer works for me. However, presentationControllerDidDismiss does not get invoked if you call it inside of the self.dismiss completion block, you need to call it before.

I adapted my code to use "presentationControllerWillDismiss" (for clarity) and simply called the delegate before I dismiss programmatically in my modals and it works great.

@IBAction func btnDismissTap(_ sender: Any) {
    if let pvc = self.presentationController {
        pvc.delegate?.presentationControllerWillDismiss?(pvc)
    }
    self.dismiss(animated: true, completion: nil)
}

Now, I no longer need to create delegate functions to handle modal dismissals in code and my swipe handler takes care of all scenarios.

FYI, what I'm "handling" is doing some UI clean up (de-selections, etc) on the presenting UI once the modal is dismissed.

like image 9
William T. Avatar answered Nov 15 '22 20:11

William T.


As @matt mentioned, there is no need to inform the one who dismissed a view by delegate. Because it is already known. BUT if you need that delegate method to be called, you should call it manually your self after dismissing the view:

@IBAction func btnDismissTap(_ sender: Any) {
    self.dismiss(animated: true) {
        presentationController?.delegate?.presentationControllerDidDismiss?(presentationController!)
    }
}
like image 8
Mojtaba Hosseini Avatar answered Nov 15 '22 20:11

Mojtaba Hosseini