问题
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 draws into this box which interferes with the gesture. It pulls the screen down instead of drawing a vertical line.
How can you disable the vertical swipe to dismiss gesture in a modal view controller presented as a sheet?
Setting isModalInPresentation = true
still allows the sheet to be pulled down, it just won't dismiss.
回答1:
For future internet travelers, in general, one shouldn't disable the swipe to dismiss functionality. But if you have a scenario where your gesture or touch handling is impacted by this feature, I did receive some advice from an Apple engineer on how to accomplish this.
If you can prevent the system's pan gesture recognizer from beginning, this will prevent the gestural dismissal. A few ways to do this:
If your canvas drawing is done with a gesture recognizer, such as your own
UIGestureRecognizer
subclass, enter thebegan
phase before the sheet’s dismiss gesture does. If you recognize as quickly asUIPanGestureRecognizer
, you will win, and the sheet’s dismiss gesture will be subverted.If your canvas drawing is done with a gesture recognizer, setup a dynamic failure requirement with
-shouldBeRequiredToFailByGestureRecognizer:
(or the related delegate method), where you returnNO
if the passed in gesture recognizer is aUIPanGestureRecognizer
.If your canvas drawing is done with manual touch handling (e.g.
touchesBegan:
), override-gestureRecognizerShouldBegin
on your touch handling view, and returnNO
if the passed in gesture recognizer is aUIPanGestureRecognizer
.
With my setup #3 proved to work very well. This allows the user to swipe down anywhere outside of the drawing canvas to dismiss (like the nav bar), while allowing the user to draw without moving the sheet, just as one would expect.
I cannot recommend trying to find the gesture to disable it, as it seems to be rather dynamic and can reenable itself when switching between different size classes for example, and this could change in future releases.
回答2:
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.
回答3:
In the presented ViewController use this in viewDidLoad:
if #available(iOS 13.0, *) {
self.isModalInPresentation = true
}
回答4:
you can change the presentation style, if its in full screen the pull down to dismiss would be disabled
navigationCont.modalPresentationStyle = .fullScreen
回答5:
You can use the UIAdaptivePresentationControllerDelegate method presentationControllerDidAttemptToDismiss and disable the gestureRecognizer on the presentedView. Something like this:
func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
presentationController.presentedView?.gestureRecognizers?.first?.isEnabled = false
}
回答6:
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
回答7:
Me, I use this :
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
for(UIGestureRecognizer *gr in self.presentationController.presentedView.gestureRecognizers) {
if (@available(iOS 11.0, *)) {
if([gr.name isEqualToString:@"_UISheetInteractionBackgroundDismissRecognizer"]) {
gr.enabled = false;
}
}
}
回答8:
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;
}
回答9:
For navigation Controller, to avoid swipe interaction for presented view we can use:
if #available(iOS 13.0, *) {navController.isModalInPresentation = true}
来源:https://stackoverflow.com/questions/58647239/disabling-interactive-dismissal-of-presented-view-controller-on-ios-13-when-drag