UIPopoverPresentationController on iOS 8 iPhone

Objective CIphoneUipopovercontrollerIos8Uipopover

Objective C Problem Overview


Does anyone know if UIPopoverPresentationController can be used to present popovers on iPhones? Wondering if Apple added this feature on iOS 8 in their attempt to create a more unified presentation controllers for iPad and iPhone.

Not sure if its OK to ask/answer questions from Beta. I will remove it in that case.

Objective C Solutions


Solution 1 - Objective C

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)

Solution 2 - Objective C

If anybody wants to present a popover with code only, you can use the following approach.

OBJECTIVE - C

Declare a property of UIPopoverPresentationController:

@property(nonatomic,retain)UIPopoverPresentationController *dateTimePopover8;

Use the following method to present the popover from UIButton:

- (IBAction)btnSelectDatePressed:(id)sender
{
    UINavigationController *destNav = [[UINavigationController alloc] initWithRootViewController:dateVC];/*Here dateVC is controller you want to show in popover*/
    dateVC.preferredContentSize = CGSizeMake(280,200);
    destNav.modalPresentationStyle = UIModalPresentationPopover;
    _dateTimePopover8 = destNav.popoverPresentationController;
    _dateTimePopover8.delegate = self;
    _dateTimePopover8.sourceView = self.view;
    _dateTimePopover8.sourceRect = sender.frame;
    destNav.navigationBarHidden = YES;
    [self presentViewController:destNav animated:YES completion:nil];
}

Use the following method to present the popover from UIBarButtonItem:

- (IBAction)btnSelectDatePressed:(id)sender
{
    UINavigationController *destNav = [[UINavigationController alloc] initWithRootViewController:dateVC];/*Here dateVC is controller you want to show in popover*/
    dateVC.preferredContentSize = CGSizeMake(280,200);
    destNav.modalPresentationStyle = UIModalPresentationPopover;
    _dateTimePopover8 = destNav.popoverPresentationController;
    _dateTimePopover8.delegate = self;
    _dateTimePopover8.sourceView = self.view;
     CGRect frame = [[sender valueForKey:@"view"] frame];
    frame.origin.y = frame.origin.y+20;
    _dateTimePopover8.sourceRect = frame;
    destNav.navigationBarHidden = YES;
    [self presentViewController:destNav animated:YES completion:nil];
}

Implement this delegate method too in your view controller:

- (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController: (UIPresentationController * ) controller {
    return UIModalPresentationNone;
}

To dismiss this popover, simply dismiss the view controller. Below is the code to dismiss the view controller:

-(void)hideIOS8PopOver
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

SWIFT

Use the following method to present the popover from UIButon:

func filterBooks(sender: UIButon)
    {
        let filterVC =  FilterDistanceViewController(nibName: "FilterDistanceViewController", bundle: nil)
        var filterDistanceViewController = UINavigationController(rootViewController: filterVC)
        filterDistanceViewController.preferredContentSize = CGSizeMake(300, 205)
        let popoverPresentationViewController = filterDistanceViewController.popoverPresentationController
        popoverPresentationViewController?.permittedArrowDirections = .Any
        popoverPresentationViewController?.delegate = self
        popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem
        popoverPresentationViewController!.sourceView = self.view;
        popoverPresentationViewController!.sourceRect = sender.frame
        
        filterDistanceViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
        filterDistanceViewController.navigationBarHidden = true
        self.presentViewController(filterDistanceViewController, animated: true, completion: nil)
    }

Use the following method to present the popover from UIBarButtonItem:

func filterBooks(sender: UIBarButtonItem)
    {
        let filterVC =  FilterDistanceViewController(nibName: "FilterDistanceViewController", bundle: nil)
        var filterDistanceViewController = UINavigationController(rootViewController: filterVC)
        filterDistanceViewController.preferredContentSize = CGSizeMake(300, 205)
        let popoverPresentationViewController = filterDistanceViewController.popoverPresentationController
        popoverPresentationViewController?.permittedArrowDirections = .Any
        popoverPresentationViewController?.delegate = self
        popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem
        popoverPresentationViewController!.sourceView = self.view;
        var frame:CGRect = sender.valueForKey("view")!.frame
        frame.origin.y = frame.origin.y+20
        popoverPresentationViewController!.sourceRect = frame
        
        filterDistanceViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
        filterDistanceViewController.navigationBarHidden = true
        self.presentViewController(filterDistanceViewController, animated: true, completion: nil)
    }

Implement this delegate method too in your view controller:

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

Please make sure to add delegate UIPopoverPresentationControllerDelegate in .h/.m/.swift file

Solution 3 - Objective C

PROBLEM: iPhone popover displays fullscreen and does not respect preferredContentSize value.

SOLUTION: Contrary to what Apple suggests in the UIPopoverPresentationController Class reference, presenting the view controller after getting a reference to the popover presentation controller and configuring it.

// Get the popover presentation controller and configure it.
//...

// Present the view controller using the popover style.
[self presentViewController:myPopoverViewController animated: YES completion: nil]; 

Solution 4 - Objective C

Make sure to implement UIAdaptivePresentationControllerDelegate

like this:

- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller {
    return UIModalPresentationNone;
}

If you don't want full-screen popovers

Solution 5 - Objective C

I've found some workaround.

On Xcode6.1, use presentationController.delegate instead of popoverPresentationController.delegate.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier compare:@"showPopOver"] == NSOrderedSame) {
        UINavigationController * nvc = segue.destinationViewController;
        UIPresentationController * pc = nvc.presentationController;
        pc.delegate = self;
    }
}

#pragma mark == UIPopoverPresentationControllerDelegate ==
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
{
    return UIModalPresentationNone;
}

In WWDC 2014 "View Controller Advancements in iOS8", below codes can show popover on iPhone.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

    UINavigationController * nvc = segue.destinationViewController;
    UIPopoverPresentationController * pvc = nvc.popoverPresentationController;
    pvc.delegate = self;
}

#pragma mark == UIPopoverPresentationControllerDelegate ==
- (UIModalPresentationStyle)adaptivePresentationStyleForPresentationController:(UIPresentationController *)controller
{
    return UIModalPresentationNone;
}

But On Xcode 6.1, these codes shows FullScreen presentation... (nvc.popoverPresentationController is nil)

I doubt it might be an Apple's bug.

Solution 6 - Objective C

In iOS 8.3 and later, use the following syntax in the UIPopoverPresentationControllerDelegate protocol to override your popup's UIModalPresentationStyle.

func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
    return .none
}

Solution 7 - Objective C

You can extend the UIPopoverPresentationControllerDelegate like this:

protocol PopoverPresentationSourceView {}
extension UIBarButtonItem : PopoverPresentationSourceView {}
extension UIView : PopoverPresentationSourceView {}

extension UIPopoverPresentationControllerDelegate where Self : UIViewController {
   func present(popover: UIViewController, 
        from sourceView: PopoverPresentationSourceView, 
        size: CGSize, arrowDirection: UIPopoverArrowDirection) {

      popover.modalPresentationStyle = .popover
      popover.preferredContentSize = size
      let popoverController = popover.popoverPresentationController
      popoverController?.delegate = self
      if let aView = sourceView as? UIView {
          popoverController?.sourceView = aView
          popoverController?.sourceRect = CGRect(x: aView.bounds.midX, y: aView.bounds.midY, width: 0, height: 0)
      } else if let barButtonItem = sourceView as? UIBarButtonItem {
          popoverController?.barButtonItem = barButtonItem
      }
      popoverController?.permittedArrowDirections = arrowDirection
      present(popover, animated: true, completion: nil)
   }
}

You can now call present(popover: from: size: arrowDirection: ) from any view controller that implements UIPopoverPresentationControllerDelegate eg.

class YourViewController : UIViewController {
    @IBAction func someButtonPressed(_ sender: UIButton) {
        let popover = SomeViewController()
        present(popover: popover, from: sender, size: CGSize(width: 280, height: 400), arrowDirection: .right)
    }
}

extension YourViewController : UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
        return .none
    }
}

 

Solution 8 - Objective C

add these two methods in your WEBVIEW class. and add

-(void) prepareForSegue: (UIStoryboardSegue * ) segue sender: (id) sender {
    // Assuming you've hooked this all up in a Storyboard with a popover presentation style
    if ([segue.identifier isEqualToString: @"showPopover"]) {
        UINavigationController * destNav = segue.destinationViewController;
        pop = destNav.viewControllers.firstObject;
        // This is the important part
        UIPopoverPresentationController * popPC = destNav.popoverPresentationController;
        popPC.delegate = self;
    }
}

- (UIModalPresentationStyle) adaptivePresentationStyleForPresentationController: (UIPresentationController * ) controller {
    return UIModalPresentationNone;
}

Solution 9 - Objective C

In the UIAdaptivePresentationControllerDelegate you must use this method:

func adaptivePresentationStyle(for: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle

instead of this:

func adaptivePresentationStyle(for: UIPresentationController) -> UIModalPresentationStyle

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDaveView Question on Stackoverflow
Solution 1 - Objective CIvan ChooView Answer on Stackoverflow
Solution 2 - Objective CDesert RoseView Answer on Stackoverflow
Solution 3 - Objective CagandiView Answer on Stackoverflow
Solution 4 - Objective CaderView Answer on Stackoverflow
Solution 5 - Objective CnomadmonadView Answer on Stackoverflow
Solution 6 - Objective CadariaView Answer on Stackoverflow
Solution 7 - Objective CUmairView Answer on Stackoverflow
Solution 8 - Objective CMongo dbView Answer on Stackoverflow
Solution 9 - Objective CJavier Glez.View Answer on Stackoverflow