禁用手势以下拉表单/页面表单模式演示


79

在iOS 13中,可以使用平移手势关闭使用表单和页面工作表样式的模式演示。在我的一张表单中,这是有问题的,因为用户会拖入该框,从而干扰手势。它将屏幕下拉而不是画一条垂直线。

如何在以图纸形式显示的模态视图控制器中禁用垂直滑动以消除手势?

设置isModalInPresentation = true仍然允许将工作表拉下,但不会关闭。


1
Apple Developer上有一个解释清楚的文档:developer.apple.com/documentation/uikit/view_controllers/…–
Stleamist

Answers:


89

通常,您不应该尝试禁用滑动以关闭功能,因为用户希望所有表单/页面在所有应用程序中的行为都相同。相反,您可能要考虑使用全屏演示样式。如果您确实想要使用无法通过滑动消除的纸张,请设置isModalInPresentation = true,但请注意,这仍允许纸张垂直向下拉,并且在释放触摸时会弹起。请查看UIAdaptivePresentationControllerDelegate文档,以在用户尝试通过滑动将其关闭时做出反应。

如果您的应用程序的手势或触摸处理受到滑动以关闭功能的影响,我确实从Apple工程师那里收到了一些有关如何解决该问题的建议。

如果可以阻止系统的平移手势识别器启动,则可以防止手势解雇。一些方法可以做到这一点:

  1. 如果您的画布绘制是使用手势识别器(例如您自己的UIGestureRecognizer子类)完成的,请began在图纸的关闭手势之前输入阶段。如果您尽快识别UIPanGestureRecognizer,您将获胜,并且工作表的关闭手势将被颠覆。

  2. 如果您的画布绘制是使用手势识别器完成的,请使用-shouldBeRequiredToFailByGestureRecognizer:(或相关的委托方法)设置动态失败要求,NO如果传入的手势识别器是,则在此处返回UIPanGestureRecognizer

  3. 如果您的画布绘制是通过手动触摸处理完成的(例如touchesBegan:),请-gestureRecognizerShouldBegin在触摸处理视图上进行覆盖,NO如果传入的手势识别器为,则返回UIPanGestureRecognizer

在我的设置3中,事实证明效果很好。这样一来,用户可以向下滑动绘图画布之外的任何位置以关闭它(例如导航栏),同时允许用户在不移动图纸的情况下进行绘图,就像人们期望的那样。

我不建议尝试找到禁用该手势的手势,因为它似乎是动态的,并且例如在不同大小的类之间切换时可以重新启用自身,并且在将来的版本中可能会更改。


1
我遵循了#2,但最终使用了委托方法gestureRecognizer(_:,shouldRecognizeSimultaneouslyWith:)来允许一些识别器一起工作,而另一些则不能。
道格

1
我似乎无法使其中任何一个起作用?对我的情况有什么建议吗?我正在展示一个带有照片选择系统的屏幕,通过拖动来选择库存的iOS照片应用程序使用的照片。这将使用平移手势识别器和UICollectionView。如果我错了,请原谅我,但是我们不是必须成为解雇识别器的代表才能使用这些方法中的任何一种吗?
simonthumper

3
#3对我的应用程序非常有用,它允许用户绘制视图。
罗里(Rory)

2
@Jordan H您能用一些代码更好地解释#3吗?
乔治,

3
做得好!很有意思。#3最适合我。
Artem Kirillov,

29

可以在模态视图控制器的 presentedView属性中。正如我调试的那样,gestureRecognizers此属性的数组只有一项,并且将其打印会导致如下所示:

UIPanGestureRecognizer:0x7fd3b8401aa0(_UISheetInteractionBackgroundDismissRecognizer);

因此,要禁用此手势,您可以执行以下操作:

let vc = UIViewController()

self.present(vc, animated: true, completion: {
  vc.presentationController?.presentedView?.gestureRecognizers?[0].isEnabled = false
})

要重新启用它,只需将其设置isEnabledtrue

vc.presentationController?.presentedView?.gestureRecognizers?[0].isEnabled = true

请注意,iOS 13仍处于测试阶段,因此可能会在即将发布的版本中添加更简单的方法。

尽管此解决方案目前似乎可以使用,但我不建议这样做,因为它在某些情况下可能无法使用,或者在将来的iOS版本中可能会更改,并可能影响您的应用。


1
这很好!也可以将其viewWillAppear放在所显示的视图控制器上-对segue很有用。🤗
乔丹^ h

1
请注意,如果您在此视图控制器中拥有滚动视图,则在到达顶部后,仍可以向下拉以关闭它。但是,如果您没有滚动视图,那就太好了。
约旦H

1
@JordanH是的!滚动视图中的另一个手势似乎也在以某种方式处理模式退出。我打印了滚动视图手势,并且有一个UISwipeDismissalGestureRecognizer。这可能是问题。
M Reza

7
UITableView也创造_UISwipeDismissalGestureRecognizer。此外,如果您使用根视图控制器创建导航控制器,将堆栈以页面/表单的形式呈现出来,然后将另一个视图控制器推入其顶部,则向下滑动到撤消手势将通过手势识别器在UIView层次结构的较高位置创建。如果没有苹果的禁止的明确支持刷卡下降到解雇触摸事件的处理,唯一可靠的解决方案(如Xcode中11的Beta 3的)是使用UIModalPresentationStyleUIModalPresentationFullScreen
加里

3
如果您想更安全地禁用禁用的手势,甚至可以按名称或类型进行搜索。for gesture in guestures where gesture.name == "_UISheetInteractionBackgroundDismissRecognizer" { gesture.isEnabled = false }
spfursich

16

在提供的ViewController中,在viewDidLoad中使用它:

if #available(iOS 13.0, *) {
    self.isModalInPresentation = true
}

7
正如问题中指出的那样,isModalInPresentation = true仍然允许将图纸拉下来,只是不放开,这可能正是您所需要的,或者取决于您的用例,这可能是有问题的,就像我的绘图画布一样。
约旦H

3
你是对的。我认为最简单的解决方案是使用旧的全屏模式样式:self.modalPresentationStyle = .fullScreen
Zoltan Vinkler

1
viewController.isModalInPresentation = true为我工作
BharathRao

1
这是正确的答案,您仍然可以保持下拉的动画效果,但是绝对不能以此关闭视图。谢谢!
Radu Ursache

1
为我工作。比删除手势识别器好。
Benoit Deldicque

11

就我而言,我有一个模态屏幕,其视图可以接收触摸以捕获客户签名。

在导航控制器中禁用手势识别器可以解决该问题,从而完全避免触发模态交互式解雇。

以下方法在我们的模式视图控制器中实现,并通过自定义签名视图中的委托进行调用。

致电touchesBegan

private func disableDismissalRecognizers() {
    navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach {
        $0.isEnabled = false
    }
}

致电touchesEnded

private func enableDismissalRecognizers() {
    navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach {
        $0.isEnabled = true
    }
}

这是显示行为的GIF: 在此处输入图片说明

标记为重复的这个问题更好地描述了我遇到的问题:从主视图中拖动时,在iOS 13上禁用交互式关闭呈现的视图控制器


8

您可以更改演示文稿的样式,如果在全屏模式下将禁用下拉菜单以关闭

navigationCont.modalPresentationStyle = .fullScreen

1
我认为这是正确的答案。实际上,我确实将此与isModalInPresentation一起使用,并且效果很好。对我来说,关键是将这些设置在父级中。当我尝试在显示的控制器中设置viewDidLoad时,它不起作用。
biomiker

3

您可以使用UIAdaptivePresentationControllerDelegate方法presentationControllerDidAttemptToDismiss并在presentedView上禁用gestureRecognizer。像这样:

func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {      
    presentationController.presentedView?.gestureRecognizers?.first?.isEnabled = false
}

3

对于每个人遇到乔丹解决方案3运行时遇到的问题。

您必须寻找呈现的ROOT ViewController,具体取决于您的视图堆栈,这可能不是您当前的视图。

我必须寻找我的导航控制器PresentationViewController。

BTW @乔丹:谢谢!

UIGestureRecognizer *gesture = [[self.navigationController.presentationController.presentedView gestureRecognizers] firstObject];
if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {
    UIPanGestureRecognizer * pan = (UIPanGestureRecognizer *)gesture;
    pan.delegate = self;
}

1

我,我用这个:

-(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;
        }
    }
}

0

将尝试更详细地描述@Jordan H建议的方法2:

1)为了能够捕获并做出关于模式表的平移手势的决定,请将其添加到视图控制器的中viewDidLoad

navigationController?.presentationController?.presentedView?.gestureRecognizers?.forEach {
   $0.delegate = self
}

2)能够使用以下命令捕捉平移手势和您自己的手势 gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:)

3)实际的决定可以进去 gestureRecognizer(_:shouldBeRequiredToFailBy:)

示例代码,如果同时存在滑动手势,则其优先于工作表的平移手势。在没有滑动手势识别器的区域中,它不会影响原始的平移手势,因此原始的“滑动以关闭”仍可以按设计工作。

extension PeopleViewController: UIGestureRecognizerDelegate {

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if gestureRecognizer === UIPanGestureRecognizer.self && otherGestureRecognizer === UISwipeGestureRecognizer.self {
            return true
        }
        return false
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

就我而言,我只有几个滑动手势识别器,所以比较类型对我来说就足够了,但是如果有更多的手势识别器,则比较手势识别器本身(以编程方式添加的手势识别器或作为界面生成器的出口)比较有意义,如下所述这份文件:https : //developer.apple.com/documentation/uikit/touches_presses_and_gestures/coordinating_multiple_gesture_recognizers/preferring_one_gesture_over_another

这是我的情况下代码的工作方式。没有它,滑动手势通常会被忽略,并且只能偶尔使用。

在此处输入图片说明


嘿,我已经尝试过您的解决方案,但是仍然有问题。当我将委托设置为viewDidAppear(因为我presentationController为nil时,我只是在道德上提出了vc)。并且,我迭代其视图的监督,以发现该视图具有PanGesture,并将其委托设置为self。然后我的vc无法向下滑动以关闭,是否还有其他方法可以解决我的问题?请帮忙
Weslie

@Weslie,它是您需要捕获的呈现视图的识别器,而不是其超级视图的识别器。尝试临时添加gestureRecognizerShouldBegin委托方法,以查看实际捕获了哪些手势并进行调试。
维塔利

0

在IOS 13中

if #available(iOS 13.0, *) {
    obj.isModalInPresentation = true
} else {
    // Fallback on earlier versions
}

0

当用户尝试滚动到滚动视图的顶端时,如果aUITableViewUICollectionView启动页面取消手势,可以通过添加一个不可见的UIRefreshControl调用来禁用此手势。endRefreshing立即。

另请参阅https://stackoverflow.com/a/58676756/2419404


0

在prepare(for:sender :)中:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == viewControllerSegueID {
        let controller = segue.destination as! YourViewController
        controller.modalPresentationStyle = .fullScreen
    }
}

或者,在初始化控制器后:

let controller = YourViewController()
controller.modalPresentationStyle = .fullScreen

0

您可能首先在处理viewDidAppear()方法中获得UIPanGestureRecognizer的引用来处理页面工作表解雇。注意,该引用在viewWillAppear()或viewDidLoad()中为nil。然后,您只需禁用它。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    presentationController?.presentedView?.gestureRecognizers?.first.isEnabled = false
}

如果您想要更多的自定义而不是完全禁用它,例如,在页面表单中使用navBar时,请将该UIPanGestureRecognizer的委托设置为您自己的视图控制器。这样,您可以通过实现以下方式在contentView中专门禁用手势识别器,同时使其在navBar区域中保持活动状态

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {}

-3

对于导航控制器,为避免呈现视图的滑动交互,我们可以使用:

if #available(iOS 13.0, *) {navController.isModalInPresentation = true}

2
不,但这不会阻止手势,这是问题所在。
马特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.