禁用呈现视图控制器的交互式解雇

Disable the interactive dismissal of presented view controller

iOS 13 引入了 modalPresentationStyle .pageSheet(及其兄弟 .formSheet)的新设计,用于模态呈现视图控制器…

…我们可以通过向下滑动呈现的视图控制器来关闭这些工作表(交互式关闭)。尽管新的 "pull-to-dismiss" 功能非常有用,但它可能并不总是令人满意的。

问题:我们怎样才能关闭交互式关闭? - 请记住,我们保持相同的演示风格。

选项 1:

viewController.isModalInPresentation = true

(禁用交互.pageSheet解雇行为。)

  • 由于 iOS 13,UIViewController 包含一个名为 isModalInPresentation 的新 属性,必须将其设置为 true 以防止交互解雇。
  • 它基本上忽略了视图控制器范围之外的事件。如果您不仅使用自动样式,还使用 ​​.popover 等演示样式,请记住这一点。
  • 这个属性默认是false

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.


选项 2:

func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
    return false
}
  • 由于 iOS 13,UIAdaptivePresentationControllerDelegate 包含一个名为 presentationControllerShouldDismiss 的新方法。
  • 仅当呈现的视图控制器未以编程方式关闭且其 isModalInPresentation 属性 设置为 false 时才会调用此方法。

Tip: Don't forget to assign presentationController's delegate.

  1. 如果您想要与以前的 iOS 版本 (< iOS13) 相同的行为,例如全屏模型演示,只需设置目标视图的演示样式控制器到 UIModalPresentationStyle.fullScreen

    let someViewController = \*VIEW CONTROLLER*\
    someViewController.modalPresentationStyle = .fullScreen
    

    如果您正在使用故事板,只需 select segua 和 select Full Screen 形成 Presentation 下拉列表。

  2. 如果您只想禁用交互式解雇并保持新的演示文稿样式,请将 UIViewController 属性 isModalInPresentation 设置为 true

    if #available(iOS 13.0, *) {
        someViewController.isModalInPresentation = true // available in IOS13
    }
    

属性 isModalInPresentation 可能会有帮助。

来自文档:

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.

你可以这样使用它:

let controller = MyViewController()
controller.isModalInPresentation = true
self.present(controller, animated: true, completion: nil)

如果您使用故事板来布局您的 UI 我发现在使用导航控制器时禁用此交互式关闭的最佳方法是将属性检查器中导航控制器的显示从自动更改为全屏。导航堆栈中的所有视图控制器将全屏显示,用户将无法将其关闭。

Attribute Inspector showing presentation option for the navigation controller

Apple 分享了一个示例代码 at this link

它使用了 isModalInPresentation 个用户的建议。

您现在可以实现交互手势识别器的委托,并在尝试同时与滑块交互时禁用交互。这样,您可以保持交互式关闭,而滑块按预期工作。

您可以像这样禁用向下滑动:

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
})

如果您有一些业务逻辑,比如在关闭之前应该填写所有字段,您应该:

ViewDidLoad 上,如果您的 ViewController 已在导航控制器中显示:

func viewDidLoad() { 
    self.navigationController?.presentationController?.delegate = self
}

如果没有,只需使用

func viewDidLoad() { 
    self.presentationController?.delegate = self
}

然后实现委托方法:

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
    }

}

所有解决方案都很好,但就我而言,我需要一个停止移动的选项。 所以这是一个代码。

如果你想阻止移动:

self.yourViewController?.presentedView?.gestureRecognizers?[0].isEnabled = false

如果你想解锁移动:

self.yourViewController?.presentedView?.gestureRecognizers?[0].isEnabled = true