Swift: Popover Dismiss Callback

Swift: Popover dismiss callback

Protocols and delegations are solution to such problems. In my case I defined a protocol and conformed the MainViewController to the protocol.

//SecondViewController
protocol MyDelegate{
func DoSomething(text:String)
}

class SecondViewController: UIViewController {

var delegate:GetTextDelegate?

var inputTextDelegate:String = ""

override func viewDidLoad() {
newText.text = inputTextDelegate
}

@IBAction func dismissPopover(sender: UIButton) {
dismissViewControllerAnimated(true, completion: nil)
//This dismisses the popover but does not notify the MainViewConroller
}
@IBAction func doneButtonAction(sender: UIButton) {
if let delegate = self.delegate {
delegate.DoSomething(newText.text)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
}

class MainViewController: UIViewController, UIPopoverPresentationControllerDelegate, MyDelegate {


override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{

if segue.identifier == "GoToSecondViewControllerSegue"
{
var vc = segue.destinationViewController as! SecondViewController
vc.delegate = self
vc.inputTextDelegate = "I'm a popover!"
}
}

func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
//...
}

func DoSomething(text: String) {
//Do whatever you want
println(text)
}

}

How to detect when a popover is dismissed in iOS 9

Not sure which method you're referring to as being deprecated but you can still use the UIPopoverPresentationControllerDelegate to achieve this. Something like:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "popoverSegue" {
let vc = segue.destinationViewController
sortVC.modalPresentationStyle = .Popover
sortVC.popoverPresentationController?.sourceRect = filterButton.bounds
sortVC.preferredContentSize = CGSizeMake(216, 150)
sortVC.popoverPresentationController!.delegate = self
}
}

And then use the

func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController)

method to handle its dismissal.

Detect when a presented view controller is dismissed

According to the docs, the presenting controller is responsible for the actual dismiss. When the presented controller dismisses itself, it will ask the presenter to do it for it. So if you override dismissViewControllerAnimated in your VC1 controller I believe it will get called when you hit cancel on VC2. Detect the dismiss and then call the super classes version which will do the actual dismiss.

As found from discussion this does not seem to work. Rather than rely on the underlying mechanism, instead of calling dismissViewControllerAnimated:completion on VC2 itself, call dismissViewControllerAnimated:completion on self.presentingViewController in VC2. This will then call your override directly.

A better approach altogether would be to have VC2 provide a block which is called when the modal controller has completed.

So in VC2, provide a block property say with the name onDoneBlock.

In VC1 you present as follows:

  • In VC1, create VC2

  • Set the done handler for VC2 as: VC2.onDoneBlock={[VC2 dismissViewControllerAnimated:YES completion:nil]};

  • Present the VC2 controller as normal using [self presentViewController:VC2 animated:YES completion:nil];

  • In VC2, in the cancel target action call self.onDoneBlock();

The result is VC2 tells whoever raises it that it is done. You can extend the onDoneBlock to have arguments which indicate if the modal comleted, cancelled, succeeded etc....

How to update view behind the popover view on dismissal of the popover?

In iOS 13 and iOS 14, you set yourself as the popover's presentation controller's delegate and implement presentationControllerDidDismiss. In iOS 12 and before, you set yourself as the popover's popover presentation controller's delegate and implement popoverPresentationControllerDidDismissPopover.

Detecting sheet was dismissed on iOS 13


Is there a way to detect that the presented view controller sheet was dismissed?

Yes.

Some other function I can override in the parent view controller rather than using some sort of delegate?

No. "Some sort of delegate" is how you do it. Make yourself the presentation controller's delegate and override presentationControllerDidDismiss(_:).

https://developer.apple.com/documentation/uikit/uiadaptivepresentationcontrollerdelegate/3229889-presentationcontrollerdiddismiss


The lack of a general runtime-generated event informing you that a presented view controller, whether fullscreen or not, has been dismissed, is indeed troublesome; but it's not a new issue, because there have always been non-fullscreen presented view controllers. It's just that now (in iOS 13) there are more of them! I devote a separate question-and-answer to this topic elsewhere: Unified UIViewController "became frontmost" detection?.

How to Dismiss a Storyboard Popover

EDIT: These problems appear to be fixed as of iOS 7.1 / Xcode 5.1.1. (Possibly earlier, as I haven't been able to test all versions. Definitely after iOS 7.0, since I tested that one.) When you create a popover segue from a UIBarButtonItem, the segue makes sure that tapping the popover again hides the popover rather than showing a duplicate. It works right for the new UIPresentationController-based popover segues that Xcode 6 creates for iOS 8, too.

Since my solution may be of historical interest to those still supporting earlier iOS versions, I've left it below.


If you store a reference to the segue's popover controller, dismissing it before setting it to a new value on repeat invocations of prepareForSegue:sender:, all you avoid is the problem of getting multiple stacking popovers on repeated presses of the button -- you still can't use the button to dismiss the popover as the HIG recommends (and as seen in Apple's apps, etc.)

You can take advantage of ARC zeroing weak references for a simple solution, though:

1: Segue from the button

As of iOS 5, you couldn't make this work with a segue from a UIBarButtonItem, but you can on iOS 6 and later. (On iOS 5, you'd have to segue from the view controller itself, then have the button's action call performSegueWithIdentifier: after checking for the popover.)

2: Use a reference to the popover in -shouldPerformSegue...

@interface ViewController
@property (weak) UIPopoverController *myPopover;
@end

@implementation ViewController
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// if you have multiple segues, check segue.identifier
self.myPopover = [(UIStoryboardPopoverSegue *)segue popoverController];
}
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
if (self.myPopover) {
[self.myPopover dismissPopoverAnimated:YES];
return NO;
} else {
return YES;
}
}
@end

3: There's no step three!

The nice thing about using a zeroing weak reference here is that once the popover controller is dismissed -- whether programmatically in shouldPerformSegueWithIdentifier:, or automatically by the user tapping somewhere else outside the popover -- the ivar goes to nil again, so we're back to our initial state.

Without zeroing weak references, we'd have to also:

  • set myPopover = nil when dismissing it in shouldPerformSegueWithIdentifier:, and
  • set ourself as the popover controller's delegate in order to catch popoverControllerDidDismissPopover: and also set myPopover = nil there (so we catch when the popover is automatically dismissed).


Related Topics



Leave a reply



Submit