11

There are two UIViewConrollers in my Storyboard: MainViewController and SecondViewController. I'm going to show SecondViewController as a popover when user taps a button called Show Popover:

enter image description here

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

    if segue.identifier == "GoToSecondViewControllerSegue"
    {
        var vc = segue.destinationViewController as! SecondViewController
        var controller = vc.popoverPresentationController

        if controller != nil
        {
            controller?.delegate = self
            vc.inputTextDelegate = "I'm a popover!"
        }
    }
}

func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
     println("done")
}

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle
{
    return .None
}

//SecondViewController
@IBAction func dismissPopover(sender: UIButton) {
     dismissViewControllerAnimated(true, completion: nil)
     //This dismisses the popover but does not notify the MainViewConroller
}

The anchor of segue is connected to a button: enter image description here

Now I have two problems:

  1. When I tap the cancel button inside the popover it dismisses the popover but does not trigger popoverPresentationControllerDidDismissPopover inside the MainViewController

  2. How can I pass data from the SecondViewController to the MainViewController, text value of a UITextView for example.

Raju Abe
  • 735
  • 2
  • 10
  • 25
Maysam
  • 7,246
  • 13
  • 68
  • 106

4 Answers4

15

Or, more simply, just call iOS's delegate method manually when you dismiss the popover manually.

    dismissViewControllerAnimated(true, completion: nil)
    popoverPresentationController?.delegate?.popoverPresentationControllerDidDismissPopover?(popoverPresentationController!)
Luke Bartolomeo
  • 551
  • 7
  • 11
11

You need to set yourself as the popOverDelegate. You have to do this in the popoverPresentationController of the destination.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    segue.destination.popoverPresentationController?.delegate = self
}

Then declare implement the delegates in your ViewController:

extension FormViewController: UIPopoverPresentationControllerDelegate {

    func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
        printBreadcrumb("Dismissed popover")
    }

}
Keara
  • 589
  • 1
  • 6
  • 17
doozMen
  • 690
  • 1
  • 7
  • 14
  • `popoverPresentationControllerDidDismissPopover` is deprecated as of iOS 13 and has been replaced by `presentationControllerDidDismiss`. – alstr Dec 14 '21 at 17:06
8

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

}
Maysam
  • 7,246
  • 13
  • 68
  • 106
-1

I have the same problem and find the answer in Apple API.

About the function popoverPresentationControllerDidDismissPopover, they say The popover presentation controller calls this method after dismissing the popover to let you know that it is no longer onscreen. The presentation controller calls this method only in response to user actions. It does not call this method if you dismiss the popover programmatically.

So we have to do it ourselves.

You can choice a block or a delegate like @Maysam did which is more heavy. Here's my way to use a block FYI.

Let's just focus on key functions.

class SecondViewController: UIViewController {

var dismissPopover: (() -> Void)?

deinit {
    if let block = self.dismissPopover {
        block()
    }
}

 @IBAction func dismissPopover(sender: UIButton) {
        dismissViewControllerAnimated(true, completion: nil)
       //This dismisses the popover but does not notify the  MainViewConroller
 }
}

I made a block, and call it well secondVC deinit.

class MainViewController: UIViewController, UIPopoverPresentationControllerDelegate, MyDelegate {


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

        if segue.identifier == "GoToSecondViewControllerSegue"
        {
            var vc = segue.destinationViewController as! SecondViewController
            vc..dismissPopover = {
                [unowned self] () in
                self.DoSomehing()
                // call your method...
            }
        }
    }


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

}

Set up the block in prepareForSegue: method, and Done.

Jerome
  • 2,114
  • 24
  • 24