In iOS 13 modal presentations using the form and page sheet style can be dismissed with a pan down gesture. This is problematic in one of my form sheets because the user dra
In prepare(for:sender:) :
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == viewControllerSegueID {
let controller = segue.destination as! YourViewController
controller.modalPresentationStyle = .fullScreen
}
}
or, after you initialize your controller:
let controller = YourViewController()
controller.modalPresentationStyle = .fullScreen
in IOS 13
if #available(iOS 13.0, *) {
obj.isModalInPresentation = true
} else {
// Fallback on earlier versions
}
This gesture can be found in the modal view controller's presentedView property. As I debugged, the gestureRecognizers
array of this property has only one item and printing it resulted in something like this:
UIPanGestureRecognizer: 0x7fd3b8401aa0 (_UISheetInteractionBackgroundDismissRecognizer);
So to disable this gesture you can do like below:
let vc = UIViewController()
self.present(vc, animated: true, completion: {
vc.presentationController?.presentedView?.gestureRecognizers?[0].isEnabled = false
})
To re-enable it simply set isEnabled
back to true
:
vc.presentationController?.presentedView?.gestureRecognizers?[0].isEnabled = true
Note that iOS 13 is still in beta so a simpler approach might be added in an upcoming release.
Although this solution seems to work at the moment, I would not recommend it as it might not work in some situations or might be changed in future iOS releases and possibly affect your app.
In my case, I have a modal screen with a view that receives touches to capture customer signatures.
Disabling the gesture recognizer in the navigation controller solved the problem, preventing the modal interactive dismissal from being triggered at all.
The following methods are implemented in our modal view controller, and are called via delegate from our custom signature view.
Called from touchesBegan
:
private func disableDismissalRecognizers() {
navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach {
$0.isEnabled = false
}
}
Called from touchesEnded
:
private func enableDismissalRecognizers() {
navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach {
$0.isEnabled = true
}
}
Here is a GIF showing the behavior:
This question, flagged as duplicate, describes better the issue I had: Disabling interactive dismissal of presented view controller on iOS 13 when dragging from the main view
For every body having problems with Jordans solution #3 running.
You have to look for the ROOT viewcontroller which is beeing presented, depending on your viewstack, this is maybe not you current view.
I had to look for my navigation controllers PresentationViewController.
BTW @Jordam: Thanks!
UIGestureRecognizer *gesture = [[self.navigationController.presentationController.presentedView gestureRecognizers] firstObject];
if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
UIPanGestureRecognizer * pan = (UIPanGestureRecognizer *)gesture;
pan.delegate = self;
}
You may first get a reference to the UIPanGestureRecognizer handling the page sheet dismissal in viewDidAppear() method. Notice that this reference is nil in viewWillAppear() or viewDidLoad(). Then you simply disable it.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
presentationController?.presentedView?.gestureRecognizers?.first.isEnabled = false
}
If you want more customization rather than disabling it completely, for example, when using a navBar within the page sheet, set the delegate of that UIPanGestureRecognizer to your own view controller. That way, you can disable the gesture recognizer exclusively in your contentView while keeping it active in your navBar region by implementing
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {}