Swift - UIPopoverController in iOS 8
The solution I implemented for this is based on an example presented in the 2014 WWDC session View Controller Advancements in iOS 8 (see the slide notes). Note that you do have to implement the adaptivePresentationStyleForPresentationController
function as a part of the UIPopoverPresentationControllerDelegate
, but that function should be outside of your sendTapped
function in your main view controller, and you must specify UIPopoverPresentationControllerDelegate
in your class declaration line in that file to make sure that your code modifies that behaviour. I also took the liberty to separate out the logic to present a view controller in a popover into its own function and added a check to make sure the function does not present the request view controller if it is already presented in the current context.
So, your solution could look something like this:
// ViewController must implement UIPopoverPresentationControllerDelegate
class TheViewController: UIViewController, UIPopoverPresentationControllerDelegate {
// ...
// The contents of TheViewController class
// ...
@IBAction func sendTapped(sender: UIBarButtonItem) {
let popView = PopViewController(nibName: "PopView", bundle: nil)
self.presentViewControllerAsPopover(popView, barButtonItem: sender)
}
func presentViewControllerAsPopover(viewController: UIViewController, barButtonItem: UIBarButtonItem) {
if let presentedVC = self.presentedViewController {
if presentedVC.nibName == viewController.nibName {
// The view is already being presented
return
}
}
// Specify presentation style first (makes the popoverPresentationController property available)
viewController.modalPresentationStyle = .Popover
let viewPresentationController = viewController.popoverPresentationController?
if let presentationController = viewPresentationController {
presentationController.delegate = self
presentationController.barButtonItem = barButtonItem
presentationController.permittedArrowDirections = .Up
}
viewController.preferredContentSize = CGSize(width: 30, height: 30)
self.presentViewController(viewController, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
}
Real world implementation
I implemented this approach for input validation on a sign up form in an in-progress app that I host on Github. I implemented it as extensions to UIVIewController
in UIViewController+Extensions.swift
. You can see it in use in the validation functions in AuthViewController.swift
. The presentAlertPopover
method takes a string and uses it to set the value of a UILabel in a GenericAlertViewController
that I have set up (makes it easy to have dynamic text popovers). But the actual popover magic all happens in the presentViewControllerAsPopover
method, which takes two parameters: the UIViewController
instance to be presented, and a UIView
object to use as the anchor from which to present the popover. The arrow direction is hardcoded as UIPopoverArrowDirection.Up
, but that wouldn’t be hard to change.
UIPopoverController, Xcode 6, IOS 8 using Swift
Here is a simple example for iOS 8. Popover are presented using adaptivity apis in iOS 8.
class PlayerInformationTableViewController: UITableViewController, UIPopoverPresentationControllerDelegate, NSFetchedResultsControllerDelegate{
...
@IBAction func addPopover(sender: UIBarButtonItem){
let playerInformationViewController = PlayerInformationViewController()
playerInformationViewController.modalPresentationStyle = .Popover
playerInformationViewController.preferredContentSize = CGSizeMake(300, 300)
let popoverPresentationViewController = playerInformationViewController.popoverPresentationController
popoverPresentationViewController?.permittedArrowDirections = .Any
popoverPresentationViewController?.delegate = self
popoverPresentationController?.barButtonItem = sender
presentViewController(playerInformationViewController, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController!) -> UIModalPresentationStyle{
return .None
}
}
How to present popover properly in iOS 8
Okay, A housemate took a look at it and figured it out:
func addCategory() {
var popoverContent = self.storyboard?.instantiateViewControllerWithIdentifier("NewCategory") as UIViewController
var nav = UINavigationController(rootViewController: popoverContent)
nav.modalPresentationStyle = UIModalPresentationStyle.Popover
var popover = nav.popoverPresentationController
popoverContent.preferredContentSize = CGSizeMake(500,600)
popover.delegate = self
popover.sourceView = self.view
popover.sourceRect = CGRectMake(100,100,0,0)
self.presentViewController(nav, animated: true, completion: nil)
}
That's the way.
You don't talk to the popover itself anymore, you talk to the view controller inside of it to set the content size, by calling the property preferredContentSize
UIPopoverController (swift)
Use
let popOver = UIPopoverController(contentViewController:imagePicker)
iOS 8: Storyboard - Delegate for UIPopoverController
Had the same issue recently. In iOS 8, you can access the UIPopoverPresentationController and set the UIPopoverPresentationControllerDelegate (rather than the UIPopoverController and -Delegate) through the popoverPresentationController
of the destinationViewController, using this code:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if let controller = segue.destinationViewController as? UIViewController {
controller.popoverPresentationController.delegate = self
}
}
How to make UIPopoverController (Swift)?
* Updated Answer *
class ViewController: UIViewController, UIPopoverControllerDelegate, UIPopoverPresentationControllerDelegate {
@IBOutlet weak var RoutineLabel: UIButton!
@IBAction func RoutineButton(sender: AnyObject) {
switch SDiPhoneVersion.deviceVersion() {
case DeviceVersion.iPad1, DeviceVersion.iPad2, DeviceVersion.iPadMini, DeviceVersion.iPad3, DeviceVersion.iPad4, DeviceVersion.iPadAir, DeviceVersion.iPadMiniRetina:
var popoverViewController = self.storyboard?.instantiateViewControllerWithIdentifier("RoutinesTableViewController") as UITableViewController
popoverViewController.modalPresentationStyle = .Popover
popoverViewController.preferredContentSize = CGSizeMake(300, 300)
let popoverPresentationViewController = popoverViewController.popoverPresentationController
popoverPresentationViewController?.permittedArrowDirections = UIPopoverArrowDirection.Up
popoverPresentationViewController?.delegate = self
popoverPresentationViewController?.sourceView = self.RoutineLabel
popoverPresentationViewController?.sourceRect = CGRectMake(RoutineLabel.frame.width / 2, RoutineLabel.frame.height,0,0)
presentViewController(popoverViewController, animated: true, completion: nil)
default:
println("iPhones")
}
}
}
Notes:
- my PopPoverViewController is a UITableViewController
- I use SDiPhoneVersion.deviceVersion to check for device version
- I use a button to trigger it
- I make the start of my pop up depend on RoutineLabel.frame
UIPopoverPresentationController on iOS 8 iPhone
You can override the default adaptive behaviour (UIModalPresentationFullScreen
in compact horizontal environment, i.e. iPhone) using the adaptivePresentationStyleForPresentationController:
method available through UIPopoverPresentationController.delegate
.
UIPresentationController
uses this method to ask the new presentation style to use, which in your case, simply returning UIModalPresentationNone
will cause the UIPopoverPresentationController
to render as a popover instead of fullscreen.
Here's an example of the popover using a segue setup in storyboard from a UIBarButtonItem
to "present modally" a UIViewController
class SomeViewController: UIViewController, UIPopoverPresentationControllerDelegate {
// override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) { // swift < 3.0
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "PopoverSegue" {
if let controller = segue.destinationViewController as? UIViewController {
controller.popoverPresentationController.delegate = self
controller.preferredContentSize = CGSize(width: 320, height: 186)
}
}
}
// MARK: UIPopoverPresentationControllerDelegate
//func adaptivePresentationStyleForPresentationController(controller: UIPresentationController!) -> UIModalPresentationStyle { // swift < 3.0
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
// Return no adaptive presentation style, use default presentation behaviour
return .None
}
}
This trick was mentioned in WWDC 2014 session 214 "View Controller Advancement in iOS8" (36:30)
UIPopoverController for iphone not working?
Edit: As stated by Soberman, since iOS 8 it is possible to present popovers on iPhone using public APIs, so this answer is probably not relevant anymore.
As stated in Apple's documentation on UIPopoverController:
Popover controllers are for use exclusively on iPad devices.
So there is no way to use this class in iPhone application unfortunately. But there are a couple of custom third-party implementations of the functionality provided by UIPopoverController which add iPhone support and more. See https://github.com/50pixels/FPPopover for example.
Edit: There also is another highly customizable popover implementation for both iPhone/iPad worth checking out: https://github.com/nicolaschengdev/WYPopoverController.
Related Topics
How to Schedule a Same Local Notification in Swift
Giving Properties of a Uibutton to a Skspritenode in Spritekit
Segue Out of Navigation Controller
How to Simultaneously Satisfy Constraints with Keyboard and Uitoolbar
Adding Custom Game Logic to Scene Kit (Swift)
How to Manage and Free Memory Through Viewcontrollers
How to Completely Reload a View So That Viewdidload Gets Run Again
Return in Function Without Return Value in Swift
Open a Users Twitter Profile Page from iOS App
iOS with Parse. Pfuser.Currentuser() Not Getting Cached. Returns Nil After App Restart
How to Create a Custom iOS Keyboard
Uilocalnotification: Playing a Custom Audio File Saved in Documents Directory
Uisearchbar Out of Screen Bounds When Navigation Bar Translucent = False