How to present popover properly in iOS 8

IosSwiftUipopovercontrollerIos8Uipopover

Ios Problem Overview


I'm trying to add a UIPopoverView to my Swift iOS 8 app, but I am unable to access the PopoverContentSize property, as the popover does not show in the correct shape. my code:

var popover: UIPopoverController? = nil 
    
    func addCategory() {
            
    var newCategory = storyboard.instantiateViewControllerWithIdentifier("NewCategory") as UIViewController
    var nav = UINavigationController(rootViewController: newCategory)
    popover = UIPopoverController(contentViewController: nav)
    popover!.setPopoverContentSize(CGSizeMake(550, 600), animated: true)
    popover!.delegate = self
    popover!.presentPopoverFromBarButtonItem(self.navigationItem.rightBarButtonItem, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}

output:

enter image description here

When I am doing the same thing through UIPopoverPresentationController, I still don't get it done. this is my code:

func addCategory() {
    
    var popoverContent = self.storyboard.instantiateViewControllerWithIdentifier("NewCategory") as UIViewController
    var nav = UINavigationController(rootViewController: popoverContent)
    nav.modalPresentationStyle = UIModalPresentationStyle.Popover
    var popover = nav.popoverPresentationController as UIPopoverPresentationController
    popover.delegate = self
    popover.popoverContentSize = CGSizeMake(1000, 300)
    popover.sourceView = self.view
    popover.sourceRect = CGRectMake(100,100,0,0)
    
    self.presentViewController(nav, animated: true, completion: nil)
    
}

I get the exact same output.

How do I customize the size of my popover? Any help would be highly appreciated!

Ios Solutions


Solution 1 - Ios

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

Solution 2 - Ios

Actually it is much simpler than that. In the storyboard you should make the viewcontroller you want to use as popover and make a viewcontroller class for it as usual. Make a segue as shown below from the object you want to open the popover, in this case the UIBarButton named "Config".

enter image description here

In the "mother viewcontroller" implement the UIPopoverPresentationControllerDelegate and the delegate method:

func popoverPresentationControllerDidDismissPopover(popoverPresentationController: UIPopoverPresentationController) {
    //do som stuff from the popover
}

Override the prepareForSeque method like this:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
     //segue for the popover configuration window
    if segue.identifier == "yourSegueIdentifierForPopOver" {
        if let controller = segue.destinationViewController as? UIViewController {
            controller.popoverPresentationController!.delegate = self
            controller.preferredContentSize = CGSize(width: 320, height: 186)
        }
    }
}

And you're done. And you can now treat the popover view as any other view, ie. add fields and what not! And you get hold of the the content controller by using the popoverPresentationController.presentedViewController method in the UIPopoverPresentationController.

Also on an iPhone you would have to overwrite

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

Solution 3 - Ios

I found a complete example of how to get this all to work so that you can always display a popover no matter the device/orientation https://github.com/frogcjn/AdaptivePopover_iOS8_Swift.

The key is to implement UIAdaptivePresentationControllerDelegate

func adaptivePresentationStyleForPresentationController(PC: UIPresentationController!) -> UIModalPresentationStyle {
    // This *forces* a popover to be displayed on the iPhone
    return .None
}

Then extend the example above (from Imagine Digital):

nav.popoverPresentationController!.delegate = implOfUIAPCDelegate

Solution 4 - Ios

Swift 2.0

Well I worked out. Have a look. Made a ViewController in StoryBoard. Associated with PopOverViewController class.

import UIKit

class PopOverViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()    
        self.preferredContentSize = CGSizeMake(200, 200)    
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: "dismiss:")    
    }    
    func dismiss(sender: AnyObject) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }
}      
    

See ViewController:

//  ViewController.swift
   
import UIKit

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate
{
    func showPopover(base: UIView)
    {
        if let viewController = self.storyboard?.instantiateViewControllerWithIdentifier("popover") as? PopOverViewController {    

            let navController = UINavigationController(rootViewController: viewController)
            navController.modalPresentationStyle = .Popover

            if let pctrl = navController.popoverPresentationController {
                pctrl.delegate = self

                pctrl.sourceView = base
                pctrl.sourceRect = base.bounds

                self.presentViewController(navController, animated: true, completion: nil)
            }
        }
    }    
    override func viewDidLoad(){
        super.viewDidLoad()
    }    
    @IBAction func onShow(sender: UIButton)
    {
        self.showPopover(sender)
    }    
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
        return .None
    }
}  

Note: The func showPopover(base: UIView) method should be placed before ViewDidLoad. Hope it helps !

Solution 5 - Ios

In iOS9 UIPopoverController is depreciated. So can use the below code for Objective-C version above iOS9.x,

- (IBAction)onclickPopover:(id)sender {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
UIViewController *viewController = [sb instantiateViewControllerWithIdentifier:@"popover"];

viewController.modalPresentationStyle = UIModalPresentationPopover;
viewController.popoverPresentationController.sourceView = self.popOverBtn;
viewController.popoverPresentationController.sourceRect = self.popOverBtn.bounds;
viewController.popoverPresentationController.permittedArrowDirections = UIPopoverArrowDirectionAny;
[self presentViewController:viewController animated:YES completion:nil]; }

Solution 6 - Ios

Here i Convert "Joris416" Swift Code to Objective-c,

-(void) popoverstart
{
    ViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"PopoverView"];
    UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:controller];
    nav.modalPresentationStyle = UIModalPresentationPopover;
    UIPopoverPresentationController *popover = nav.popoverPresentationController;
    controller.preferredContentSize = CGSizeMake(300, 200);
    popover.delegate = self;
    popover.sourceView = self.view;
    popover.sourceRect = CGRectMake(100, 100, 0, 0);
    popover.permittedArrowDirections = UIPopoverArrowDirectionAny;
    [self presentViewController:nav animated:YES completion:nil];
}

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

Remember to ADD
UIPopoverPresentationControllerDelegate, UIAdaptivePresentationControllerDelegate

Solution 7 - Ios

This is best explained on the iOS8 Day-by-Day blog

In short, once you've set your UIViewController's modalPresentationStyle to .Popover, you can get hold of a UIPopoverPresentationClass (a new iOS8 class) via the controller's popoverPresentationController property.

Solution 8 - Ios

I made an Objective-C version of Imagine Digitals swift answer above. I don't think I missed anything as it seems to work under preliminary testing, if you spot something let me know, and I'll update it

-(void) presentPopover
{
    YourViewController* popoverContent = [[YourViewController alloc] init]; //this will be a subclass of UIViewController
    UINavigationController* nav =  [[UINavigationController alloc] initWithRootViewController:popoverContent];
    nav.modalPresentationStyle = UIModalPresentationPopover;
    UIPopoverPresentationController* popover = nav.popoverPresentationController;
    popoverContent.preferredContentSize = CGSizeMake(500,600);
    popover.delegate = self;
    popover.sourceRect = CGRectMake(100,100,0,0); //I actually used popover.barButtonItem = self.myBarButton;

    [self presentViewController:nav animated:YES completion:nil];
}

Solution 9 - Ios

my two cents for xcode 9.1 / swift 4.

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {

    override func viewDidLoad(){
	    super.viewDidLoad()
	
	    let when = DispatchTime.now() + 0.5

	    DispatchQueue.main.asyncAfter(deadline: when, execute: { () -> Void in
		    // to test after 05.secs... :)
		    self.showPopover(base: self.view)

	    })

}


func showPopover(base: UIView) {
	if let viewController = self.storyboard?.instantiateViewController(withIdentifier: "popover") as? PopOverViewController {
		
		let navController = UINavigationController(rootViewController: viewController)
		navController.modalPresentationStyle = .popover
		
		if let pctrl = navController.popoverPresentationController {
			pctrl.delegate = self
			
			pctrl.sourceView = base
			pctrl.sourceRect = base.bounds
			
			self.present(navController, animated: true, completion: nil)
		}
	}
}


@IBAction func onShow(sender: UIButton){
	self.showPopover(base: sender)
}

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

and experiment in:

func adaptivePresentationStyle...

	return .popover

or: return .pageSheet.... and so on..

Solution 10 - Ios

Implement UIAdaptivePresentationControllerDelegate in your Viewcontroller. Then add :

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

Solution 11 - Ios

The following has a pretty comprehensive guide on how to configure and present popovers. https://www.appcoda.com/presentation-controllers-tutorial/

In summary, a viable implementation (with some updates from the original article syntax for Swift 4.2), to then be called from elsewhere, would be something like the following:

func showPopover(ofViewController popoverViewController: UIViewController, originView: UIView) {
    popoverViewController.modalPresentationStyle = UIModalPresentationStyle.popover
    if let popoverController = popoverViewController.popoverPresentationController {
        popoverController.delegate = self
        popoverController.sourceView = originView
        popoverController.sourceRect = originView.bounds
        popoverController.permittedArrowDirections = UIPopoverArrowDirection.any
    }
    self.present(popoverViewController, animated: true)
}

A lot of this was already covered in the answer from @mmc, but the article helps to explain some of those code elements used, and also show how it could be expanded.

It also provides a lot of additional detail about using delegation to handle the presentation style for iPhone vs. iPad, and to allow dismissal of the popover if it's ever shown full-screen. Again, updated for Swift 4.2:

func adaptivePresentationStyle(for: UIPresentationController) -> UIModalPresentationStyle {
    //return UIModalPresentationStyle.fullScreen
    return UIModalPresentationStyle.none
}
    
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
    if traitCollection.horizontalSizeClass == .compact {
        return UIModalPresentationStyle.none
        //return UIModalPresentationStyle.fullScreen
    }
    //return UIModalPresentationStyle.fullScreen
    return UIModalPresentationStyle.none
}
    
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
    switch style {
    case .fullScreen:
        let navigationController = UINavigationController(rootViewController: controller.presentedViewController)
        let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.done, target: self, action: #selector(doneWithPopover))
        navigationController.topViewController?.navigationItem.rightBarButtonItem = doneButton
        return navigationController
    default:
        return controller.presentedViewController
    }
}
    
// As of Swift 4, functions used in selectors must be declared as @objc
@objc private func doneWithPopover() {
    self.dismiss(animated: true, completion: nil)
}

Hope this helps.

Solution 12 - Ios

 var popoverContent = (self.storyboard?.instantiateViewController(withIdentifier: "WhatsNewViewController"))! as UIViewController
        var nav = UINavigationController(rootViewController: popoverContent)
    nav.modalPresentationStyle = UIModalPresentationStyle.popover
        var popover = nav.popoverPresentationController
        popoverContent.preferredContentSize = CGSize(width: 500, height: 100)
        popover?.delegate = self
        popover?.sourceView = self.view
        popover?.sourceRect = CGRectMake(100,100,0,0)

    self.present(nav, animated: true, completion: nil)

Solution 13 - Ios

For those who wants to study!

I created an Open Source project for those who want to study and use Popover view for anypurpose. You can find the project here. https://github.com/tryWabbit/KTListPopup

KTListNewResize

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
QuestionJoris416View Question on Stackoverflow
Solution 1 - IosJoris416View Answer on Stackoverflow
Solution 2 - IosLars ChristoffersenView Answer on Stackoverflow
Solution 3 - IosDavid HuntView Answer on Stackoverflow
Solution 4 - IosAlvin GeorgeView Answer on Stackoverflow
Solution 5 - IosVijayView Answer on Stackoverflow
Solution 6 - Iosuser2941395View Answer on Stackoverflow
Solution 7 - IosClafouView Answer on Stackoverflow
Solution 8 - IosnarcoView Answer on Stackoverflow
Solution 9 - IosingcontiView Answer on Stackoverflow
Solution 10 - IosNyakibaView Answer on Stackoverflow
Solution 11 - IosTheNeilView Answer on Stackoverflow
Solution 12 - Iosuser1828845View Answer on Stackoverflow
Solution 13 - IostryKuldeepTanwarView Answer on Stackoverflow