Disable the interactive dismissal of presented view controller
IosUiviewcontrollerUikitModalviewcontrollerIos13Ios Problem Overview
iOS 13 introduces a new design of modalPresentationStyle
.pageSheet
(and its sibling .formSheet
) for modally presented view controllers…
…and we can dismiss these sheets by sliding the presented view controller down (interactive dismissal). Although the new "pull-to-dismiss" feature is pretty useful, it may not always be desirable.
THE QUESTION: How can we turn the interactive dismissal off? - Bear in mind we keep the presentation style the same.
Ios Solutions
Solution 1 - Ios
Option 1:
viewController.isModalInPresentation = true
(Disabled interactive .pageSheet
dismissal acts like this.)
- Since the iOS 13,
UIViewController
contains a new property calledisModalInPresentation
which must be set totrue
to prevent the interactive dismissal. - It basically ignores events outside the view controller's bounds. Bear that in mind if you are using not only the automatic style but also presentation styles like
.popover
etc. - This property is
false
by default.
> From the official docs: If true
, UIKit ignores events outside the view controller's bounds and prevents the interactive dismissal of the view controller while it is onscreen.
Option 2:
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
return false
}
- Since the iOS 13,
UIAdaptivePresentationControllerDelegate
contains a new method calledpresentationControllerShouldDismiss
. - This method is called only if the presented view controller is not dismissed programmatically and its
isModalInPresentation
property is set tofalse
.
> Tip: Don't forget to assign presentationController's delegate.
Solution 2 - Ios
-
If you want the same behaviour as it's in previous iOS version (< iOS13) like model presentation in fullscreen, just set the presentation style of your destination view controller to
UIModalPresentationStyle.fullScreen
let someViewController = \*VIEW CONTROLLER*\ someViewController.modalPresentationStyle = .fullScreen
And if you are using storyboard just select the segua and select
Full Screen
form thePresentation
dropdown.
-
If you just want to disable the interactive dismissal and keep the new presentation style set
UIViewController
propertyisModalInPresentation
totrue
.if #available(iOS 13.0, *) { someViewController.isModalInPresentation = true // available in IOS13 }
Solution 3 - Ios
The property isModalInPresentation
might help.
From the documentation:
> When you set it to true
, UIKit ignores events outside the view controller's bounds and prevents the interactive dismissal of the view controller while it is onscreen.
You can use it like this:
let controller = MyViewController()
controller.isModalInPresentation = true
self.present(controller, animated: true, completion: nil)
Solution 4 - Ios
If you have some business logic, something like all fields should be filled before dismissing, you should:
On ViewDidLoad
if your ViewController has been presented within a Navigation Controller:
func viewDidLoad() {
self.navigationController?.presentationController?.delegate = self
}
If not, simply use
func viewDidLoad() {
self.presentationController?.delegate = self
}
Then implement the delegate method:
extension ViewController: UIAdaptivePresentationControllerDelegate {
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
guard let text = firstName.text, text.isEmpty else { return false }
guard let text = lastName.text, text.isEmpty else { return false }
...
return true
}
}
Solution 5 - Ios
If you are using storyboards to layout your UI I have found the best way to disable this interactive dismissal when using a navigation controller is to change the presentation of the Navigation Controller in the attribute inspector from Automatic to Full Screen. All view controllers in your navigation stack will then be full screen and will not be able to be dismissed by the user.
Attribute Inspector showing presentation option for the navigation controller
Solution 6 - Ios
You can now implement the delegate for the interaction gesture recognizer and disable the interaction if there is an attempt to simultaneously interact with the slider. This way, you keep the interactive dismiss, while the slider works as expected.
You can disable the swipe down like this:
let controller = storyboard?.instantiateViewController(withIdentifier: "NextVC") as! NextVC
let navigationController = UINavigationController(rootViewController: controller)
self.present(navigationController, animated: true, completion: {
navigationController.presentationController?.presentedView?.gestureRecognizers?[0].isEnabled = false
})
Solution 7 - Ios
Apple shared a sample code about it at this link
It uses isModalInPresentation
as many users suggestion.
Solution 8 - Ios
All solutions are good, but in my case, I need an option to stop movement. So this is a code for that.
if you want to block movement:
self.yourViewController?.presentedView?.gestureRecognizers?[0].isEnabled = false
And if you want to unblock movement:
self.yourViewController?.presentedView?.gestureRecognizers?[0].isEnabled = true