Search code examples
iosswiftuipopovercontroller

How to "wait' for a PopoverPresentationController to be dismissed


I am writing a "notebook" style program. Notebooks have multiple pages and I am trying to put in a "go to page" popover to allow the user to go to any page. The popover presents a collectionView of thumbnails of each page. Obviously, it would be great to:

  1. Click on desired page thumbnail in popover
  2. Send the selectedPage via delegate
  3. Have the recipient mainViewController display the selectedPage

The popover works great. The page selection works and passes the selectedPage back.

The problem is that I need to have the mainViewController "wait" for the user to select a page. Here the relevant sections of code in the mainViewController:

User selects barButton to show popover:

@IBAction func selectPage(sender: UIBarButtonItem) {

        self.performSegueWithIdentifier("showPages", sender: self)

    //need to wait here for the popover to be dismissed.
//the next line is executed before segue even appears
//"while" and delay don't work

    imageView.image = currentNotebook.pages[goToPage! - 1] //displays selectedPage (goToPage is set by delegation)
    goToPage = nil
}

//segue to popover

case "showPages" :
            let navigationController = segue.destinationViewController as? PageCollectionViewController

                if let vc = navigationController {
                    vc.delegate = self
                    vc.modalInPopover = false
                    vc.preferredContentSize = CGSizeMake(400,100)
                    vc.notebook = self.currentNotebook
                    print("got to show pages")
        }

I suspect I need some sort of closure or handler in the selectPage function, but I can't figure it out. Hope that is clear enough. It is very early in the morning...

Here is the code for the popover:

import UIKit

protocol PageCollectionViewControllerDelegate {
func selectsPage(selectedPage:Int)
}

class PageCollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate  {

var notebook: Notebook!
var pageNum: Int!
var delegate: PageCollectionViewControllerDelegate?

@IBOutlet weak var pageCollectionView: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()

    self.pencilCollectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: "cell")

    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func numberOfSectionsInCollectionView(pageCollectionView: UICollectionView) -> Int {    
    return 1
}


func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

    return notebook.pages.count
}

func collectionView(pageCollectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {

    let cell = pageCollectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! PageCollectionViewCell
    let cellImage = notebook.pages[indexPath.row]
    let tempThumb = imageWithImage(cellImage, scaledToFillSize: CGSizeMake(cell.bounds.width, cell.bounds.height)) //create thumbnail of each page

    cell.pageThumb.image = tempThumb
    cell.backgroundColor = UIColor.whiteColor()

    return cell
}

func collectionView(pageCollectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {

    //return page number

    if let delegate =  self.delegate {
        pageNum = indexPath.row + 1
        print("page number",pageNum)

        delegate.selectsPage(pageNum)
    }
    self.dismissViewControllerAnimated(true, completion: nil) //if you comment this out the popover is  not dismissed when clicking on a cell
}


func imageWithImage(image: UIImage, scaledToFillSize size: CGSize) -> UIImage {
    let scale: CGFloat = max(size.width / image.size.width, size.height / image.size.height)
    let width: CGFloat = image.size.width * scale
    let height: CGFloat = image.size.height * scale
    let imageRect: CGRect = CGRectMake((size.width - width) / 2.0, (size.height - height) / 2.0, width, height)
    UIGraphicsBeginImageContextWithOptions(size, false, 0)
    image.drawInRect(imageRect)
    let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return newImage
}

}

Solution

  • This is a typical example of async programming. In this case, the async event you're waiting for is a user response.

    The almost universal answer is "don't wait. Send a message and have the event notify you when it's done."

    What I would do is create a custom subclass of UIViewController as your popover, and either give the popover a completion block (closure) property, or set it up with a delegate, and define a protocol so that the popover can notify it's delegate when the user selects an option. (The two approaches are quite similar, but with a completion block you don't have to define a method that gets invoked - you just pass in your completion code when you make the call that invokes the popover.)