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

时间:2019-06-22 19:36:59

标签: ios uigesturerecognizer ios13

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

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

设置isModalInPresentation = true仍然可以将工作表拉下,只是不会消失。

15 个答案:

答案 0 :(得分:38)

通常,对于将来的互联网旅行者来说,不应禁用滑动以关闭功能。但是,如果您的手势或触摸处理受到此功能的影响,我确实从Apple工程师那里收到了一些有关如何完成此操作的建议。

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

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

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

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

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

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

答案 1 :(得分:5)

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

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

答案 2 :(得分:4)

无需重新发明轮子。就像在 destinationViewController 上采用 UIAdaptivePresentationControllerDelegate 协议然后实现相关方法一样简单:

func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
    return false
}

例如,假设您的 destinationViewController 已准备好进行如下 segue:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "yourIdentifier",
       let destinationVC = segue.destination as? DetailViewController
    {
        //do other stuff

        destinationVC.presentationController?.delegate = destinationVC

    }
}

然后在 destinationVC(应该采用上述协议)上,您可以实现所描述的方法 func presentationControllerShouldDismiss(_ presentationController:) -> Bool 或任何其他方法,以便正确处理您的自定义行为。

答案 3 :(得分:3)

可以在模式视图控制器的presentedView属性中找到此手势。如我所调试的,此属性的gestureRecognizers数组只有一项,并且打印它会导致如下所示:

  

UIPanGestureRecognizer:0x7fd3b8401aa0   (_UISheetInteractionBackgroundDismissRecognizer);

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

let vc = UIViewController()

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

要重新启用它,只需将isEnabled设置回true

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

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

答案 4 :(得分:3)

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

navigationCont.modalPresentationStyle = .fullScreen

答案 5 :(得分:1)

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

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

答案 6 :(得分:1)

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

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

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

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: enter image description here

标记为重复的这个问题更好地描述了我遇到的问题:Disabling interactive dismissal of presented view controller on iOS 13 when dragging from the main view

答案 7 :(得分:0)

我,我用这个:

-(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 :(得分:0)

对于每个在Jordans解决方案#3运行中遇到问题的人。

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

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

顺便说一句@乔丹:谢谢!

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

答案 9 :(得分: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

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

enter image description here

答案 10 :(得分:0)

在IOS 13中

    draw.rect(BACKGROUND, tile_color, (WIDTH*.43, HEIGHT*4.99, WIDTH, HEIGHT), 2)
for column in range (2, 6, 1):
draw.rect(BACKGROUND, tile_color, (WIDTH*1.43, HEIGHT*(column+1.5), WIDTH, HEIGHT), 2)
for column in range (1, 8, 1):
draw.rect(BACKGROUND, tile_color, (WIDTH*2.43, HEIGHT*(column+1), WIDTH, HEIGHT), 2)
for column in range (1, 7, 1):
draw.rect(BACKGROUND, tile_color, (WIDTH*3.43, HEIGHT*(column+1.5), WIDTH, HEIGHT), 2)
for column in range (1, 8, 1):
draw.rect(BACKGROUND, tile_color, (WIDTH*4.43, HEIGHT*(column+1), WIDTH, HEIGHT), 2)        
for column in range (1, 7, 1):
draw.rect(BACKGROUND, tile_color, (WIDTH*5.43, HEIGHT*(column+1.5), WIDTH, HEIGHT), 2)
for column in range (1, 8, 1):
draw.rect(BACKGROUND, tile_color, (WIDTH*6.43, HEIGHT*(column+1), WIDTH, HEIGHT), 2)
for column in range (2, 6, 1):
draw.rect(BACKGROUND, tile_color, (WIDTH*7.43, HEIGHT*(column+1.5), WIDTH, HEIGHT), 2)
draw.rect(BACKGROUND, tile_color, (WIDTH*8.4, HEIGHT*4.99, WIDTH, HEIGHT), 2)

答案 11 :(得分:0)

如果用户尝试滚动到滚动视图的顶端时,UITableViewUICollectionView启动页面取消手势,则可以通过添加不可见的{ {1}}立即调用UIRefreshControl

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

答案 12 :(得分: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

答案 13 :(得分:0)

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

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

例如,如果要进行更多自定义设置而不是完全禁用它,则在页面表单中使用navBar时,请将该UIPanGestureRecognizer的委托设置为您自己的视图控制器。这样,您可以通过实现

在内容视图中专门禁用手势识别器,同时使其在navBar区域中保持活动状态。
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {}

答案 14 :(得分:-1)

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

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