iOS 13 的 presentationControllerDidDismiss() 未在 Compact 环境中为 Popover 调用

iOS 13's presentationControllerDidDismiss() Not Called for Popover in Compact Environment

我正在为 iOS 13 的新“卡片式”模态视图更新我的应用程序。使用 UIAdaptivePresentationControllerDelegatepresentationControllerDidAttemptToDismiss()presentationControllerDidDismiss() 函数,一切都运行良好。但是,对于将 .modalPresentationStyle 设置为 .popover 的视图,在紧凑环境(例如 phone 或 iPad 中呈现时不会调用 presentationControllerDidDismiss()拆分或滑过)。在常规尺寸 class 环境(例如 iPad 全屏)中显示时,它会被正确调用。

我的代码设置非常简单:

呈现弹出窗口的代码:

func showChooser() {
    // other setup code...
    navController.modalPresentationStyle = .popover
    navController.popoverPresentationController?.barButtonItem = self.viewController?.navigationItem.leftBarButtonItem
    self.present(navController, animated: true)
}

然后,呈现的控制器符合UIAdaptivePresentationControllerDelegate并设置:

// This is in the presented view controller (i.e. the popover)
override func viewDidLoad() {
    // other setup removed for brevity…
    self.navigationController?.presentationController?.delegate = self
}

func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
    print("did dismiss")
    self.cancel?()
}

当视图在常规尺寸 -class 环境中呈现时,它会正确显示为弹出窗口。当用户在弹出窗口外点击时,将调用 presentationControllerDidDismiss()。但是,当相同的代码在紧凑的环境中呈现时,它可以正确显示(作为卡片样式),但是当用户向下拖动视图时,不会调用presentationControllerDidDismiss()

如果我将 .modalPresentationStyle 更改为其他内容,例如 .pageSheet.formSheet,那么它在紧凑或常规演示文稿中都会按预期工作。

我试过在紧凑环境下使用委托的 adaptivePresentationStyle() 将样式更改为 .formSheet,但仍然无法正确调用 presentationControllerDidDismiss()

更新: 我应该提到我当前的解决方法是检查大小 class 并根据需要更改 .modalPresentationStyle

if self.traitCollection.horizontalSizeClass == .compact {
    navController.modalPresentationStyle = .automatic
} else {
    navController.modalPresentationStyle = .popover
    navController.popoverPresentationController?.barButtonItem = self.viewController?.navigationItem.leftBarButtonItem
}

这行得通,但似乎只使用 .popover 样式应该可以正确适应并调用正确的委托方法。

更新二: 我更新了上面的代码以阐明 presented 视图控制器是处理委托方法的视图控制器。

此外,在深入研究之后,我注意到如果 presenting 视图控制器是委托并处理委托方法,那么这一切都按预期工作。由于它也适用于紧凑环境中所有 .modalPresentationStyleexcept 弹出窗口的 presented 视图控制器,因此可能存在一些生命周期问题何时以这种方式显示弹出窗口?

关于我可能做错了什么的想法?

问题只是时间问题之一。您在第二个视图控制器中执行此操作:

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

晚了。您可以在配置和执行演示的地方设置委托:

func showChooser() {
    navController.modalPresentationStyle = .popover
    navController.popoverPresentationController?.barButtonItem = 
        self.viewController?.navigationItem.leftBarButtonItem
    navController.presentationController?.delegate = // *
        navController.viewControllers[0] 
        as! UIAdaptivePresentationControllerDelegate
    self.present(navController, animated: true)
}

如果您更愿意坚持让第二个视图控制器将自己设置为委托,请早点进行。第一个好机会是willMove:

override func willMove(toParent parent: UIViewController?) {
    self.parent?.presentationController?.delegate = self
}

navController.presentationController?.delegate = // * navController.viewControllers[0]放在self.present(navController, animated: true)之后,否则你的presentationController可能是nil

非常感谢该示例 - 只是为了扩展 Matt 的详细信息并为那些寻找通用示例(通过创建独立 viewController)的人带来好处,我相信如下所示也应该有效:

func presentExampleViewController() {
    // Any other setup code specific to the view in your app can go here...
    let exampleViewController = SomeCustomViewController()
    exampleViewController.presentationController?.delegate = exampleViewController
    self.present(exampleViewController, animated: true)
}

如果您的 viewController 嵌套在另一个导航中viewController

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