当一个呈现的视图控制器被手势解散后如何接收通知?

7

在某些情况下(iPhone X,iOS 13),可以通过从顶部拉动来使用手势来取消已呈现的视图控制器。

在这种情况下,我似乎找不到一种方法来通知呈现视图控制器。我错过了什么吗?

我唯一发现的方法是将一个委托方法添加到已呈现的视图控制器的viewDidDisappear中。

例如:

class Presenting: UIViewController, PresentedDelegate {
    func someAction() {
        let presented = Presented()
        presented.delegate = self
        present(presented, animated: true, completion: nil)
    }
    func presentedDidDismiss(_ presented: Presented) {
        // Presented was dismissed
    }
}

protocol PresentedDelegate: AnyObject {
    func presentedDidDismiss(_ presented: Presented)
}

class Presented: UIViewController {
    weak var delegate: PresentedDelegate?

    override func viewDidDisappear(animated: Bool) {
         ...
         delegate?.presentedDidDismiss(self)
    }
}

通过通知(notifications)管理这个问题也是可能的,使用vc子类,但这仍然不够理想。


extension Notification.Name {
    static let viewControllerDidDisappear = Notification.Name("UIViewController.viewControllerDidDisappear")
}

open class NotifyingViewController: UIViewController {

    override open func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        NotificationCenter.default.post(name: .viewControllerDidDisappear, object: self)
    }
}

一定有更好的方法来实现这个?


你是指iOS 13的演示吗? - emrcftci
有些 iPhone 可以通过手势来取消显示的视图控制器。在运行 13.0 及以上版本的设备上,这是取消 VC 的默认方式。 - Keshu R.
是的,我会编辑原始问题。 - gdollardollar
2个回答

14

从iOS 13开始,苹果公司推出了一种新的方式让用户通过从顶部向下拖动来 dismiss 展示的视图控制器。您可以通过将UIAdaptivePresentationControllerDelegate实现到您正在呈现的UIViewController上来捕获此事件,在这种情况下是Presenting控制器。然后您可以在presentationControllerDidDismiss方法中收到有关此事件的通知。以下是代码示例:-

class Presenting: UIViewController, UIAdaptivePresentationControllerDelegate {

    func someAction() {
        let presented = Presented()
        presented.presentationController?.delegate = self
        present(presented, animated: true, completion: nil)
    }

    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        // Only called when the sheet is dismissed by DRAGGING.
        // You'll need something extra if you call .dismiss() on the child.
        // (I found that overriding dismiss in the child and calling
        // presentationController.delegate?.presentationControllerDidDismiss
        // works well).
    }
}

注意:

  1. 此方法仅在从顶部向下滑动以解除操作时触发,而不适用于编程方式的dismiss(animated:completion:)方法。
  2. 您不需要任何自定义代理或Notification观察者来获取用户通过向下滑动关闭控制器的事件,因此可以将它们删除。

太棒了,它能运行。我会点赞的,但显然我没有足够的声望。 很遗憾它只适用于iOS 13,但我想在那之前似乎没有标准的动画解散。谢谢! - gdollardollar
为什么presentationControllerDidDismiss是公开的? - Jawad Ali
在SO中,您需要超过15个声望才能进行投票。在iOS13之前,用户无法通过滑动来关闭视图控制器,因此没有必要将其用于<iOS13。 - Frankenstein
@jawadAli 谢谢,我没有注意到。双向都可以工作。 - Frankenstein

5

采用 UIAdaptivePresentationControllerDelegate 并实现 presentationControllerDidAttemptToDismiss 方法(iOS 13+)

extension Presenting : UIAdaptivePresentationControllerDelegate {

    func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
       presentationController.presentingViewController.presentedDidDismiss(self)
    }
}

UIPresentationController有一个属性presentingViewController。名称自解。您不需要显式的委托协议。

实际上,该方法被称为能够显示对话框,例如在关闭控制器之前保存更改。您还可以实现presentationControllerDidDismiss()

不要向彼此相关的控制器发布通知。这是不好的做法。


1
@jawadAli 是的,这个方法已经在iOS 13中引入了。在iOS 12及以下版本中会调用viewWillDisappear - vadian
感谢您提供正确的解决方案!我接受了@Frankenstein的解决方案,因为它澄清了委托+使用presentationControllerDidDismiss,这在我的情况下更加合适! - gdollardollar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接