Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 583
gdollardollar Avatar asked Jun 12 '20 07:06

gdollardollar


People also ask

How do you dismiss a popover in Swift?

It is an array of views in the interface behind the popover; the user can interact with these views, but a tap anywhere else outside the popover will dismiss it (with no effect on the thing tapped). If passThroughViews is nil, a tap anywhere outside the popover will dismiss it.

What is UIViewController in IOS?

The UIViewController class defines the shared behavior that's common to all view controllers. You rarely create instances of the UIViewController class directly. Instead, you subclass UIViewController and add the methods and properties needed to manage the view controller's view hierarchy.


2 Answers

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.
like image 127
Frankenstein Avatar answered Nov 14 '22 15:11

Frankenstein


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.

like image 42
vadian Avatar answered Nov 14 '22 13:11

vadian