iPhone Popover

on

As of iOS 8 it is now possible to present a real popover on an iPhone. I'll show two examples, one with a segue in the storyboard and the other from code

iPhone Popover - Storyboard Segue

In the storyboard CTRL-click to drag from the presenting controller to your controller you want as a popover.

Now I'll create a function that takes in the needed parameters to be able to prepare the content controller (that will appear in the popover) to be in the popover.


	  func preparePopover(contentController: UIViewController,
	                                 sender: UIView,
	                               delegate: UIPopoverPresentationControllerDelegate?) {
	  
	      contentController.modalPresentationStyle = .Popover
	      contentController.popoverPresentationController!.sourceView = sender
	      contentController.popoverPresentationController!.sourceRect = sender.bounds
	      contentController.popoverPresentationController!.delegate = delegate
	  }

	

If you run that code as is you'll notice that in a compact width (ie: iPhone portrait) the popover actually fills the screen. If we want to incorporate custom behavior for this we can and that's why we are taking in an option delegate parameter. Let's assume we always want to make it look like a popover:

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {

    func preparePopover(contentController: UIViewController,
	                           sender: UIView,
	                         delegate: UIPopoverPresentationControllerDelegate?) {

        contentController.modalPresentationStyle = .Popover
        contentController.popoverPresentationController!.sourceView = sender
        contentController.popoverPresentationController!.sourceRect = sender.bounds
        contentController.popoverPresentationController!.delegate = delegate
    }
    
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
	  
        super.prepareForSegue(segue, sender: sender)
        
        preparePopover(segue.destinationViewController, sender: sender as! UIView, delegate: self)
    }

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

Notice in the code above that we are providing the delegate callback to tell it to use adaptive presentation style "None", so it stays as a default popover. Now it appears like we expected it to:

Progammatically in code

Doing this behavior in code is just as easy, we just need to do the presentation part manually:

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {

    func preparePopover(contentController: UIViewController,
	                           sender: UIView,
	                         delegate: UIPopoverPresentationControllerDelegate?) {

        contentController.modalPresentationStyle = .Popover
        contentController.popoverPresentationController!.sourceView = sender
        contentController.popoverPresentationController!.sourceRect = sender.bounds
        contentController.popoverPresentationController!.delegate = delegate
    }
    
    func presentPopover(sender: UIView) {
	  
        let viewController = UIViewController()
        preparePopover(viewController, sender: sender, delegate: self)
        presentViewController(viewController, animated: true, completion: nil)
        
    }

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

What about adapting?

Since the view controller can be the delegate, it can decide what presentation style to do based on the circumstances it finds itself in. For this reason there is an optional parameter to give you the size class:

	//...  
	     func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        return (traitCollection.horizontalSizeClass == .Compact) ? .Popover : .None
    }
}
	  

The "adapting" code above just essentially does what the default behavior is. Apple makes the assumption that the default behavior is to fill the whole screen rather than as a real popover. Its kind of backwards a little bit since return .Popover seems like it would be a popover but its .None that forces it to be a popover.