简体   繁体   中英

How to get notified when a presented view controller is dismissed with a gesture?

It is possible in some cases (iPhone X, iOS 13) to dismiss presented view controllers with a gesture, by pulling from the top.

In that case, I can't seem to find a way to notify the presenting view controller. Did I miss something?

The only I found would be to add a delegate method to the viewDidDisappear of the presented view controller.

Something like:

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

It is also possible to manage this via notifications, using a vc subclass but it is still not satisfactory.


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

There must be a better way to do this?

From iOS 13 Apple has introduced a new way for the users to dismiss the presented view controller by pulling it down from the top. This event can be captured by implementing the UIAdaptivePresentationControllerDelegate to the UIViewController you're presenting on, in this case, the Presenting controller. And then you can get notified about this event in the method presentationControllerDidDismiss . Here is the code example:-

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

Note:

  1. This method only gets triggered for dismissing by swiping from the top and not for the programmatic dismiss(animated:,completion:) method.
  2. You don't need any custom delegate or Notification observer for getting the event where the user dismisses the controller by swiping down, so you can remove them.

Adopt UIAdaptivePresentationControllerDelegate and implement presentationControllerDidAttemptToDismiss (iOS 13+)

extension Presenting : UIAdaptivePresentationControllerDelegate {

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

UIPresentationController has a property presentingViewController . The name is self-explanatory. You don't need the explicit delegate protocol.

The method is actually called to be able to show a dialog for example to save changes before dismissing the controller. You can also implement presentationControllerDidDismiss()

And do not post notifications to controllers which are related to each other. That's bad practice.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM