4
votes

Generally I followed the instructions here: https://stackoverflow.com/a/24687152/3741933.

However, as discussed in its comments, the popover is always fullscreen regardless of preferredContentSize or sourceRect.

The button to present the popover:

func buttonClicked(sender: UIButton!) {
    let ac = EmptyViewController() as UIViewController
    ac.modalPresentationStyle = .Popover
    ac.preferredContentSize = CGSizeMake(200, 200)
    let popover = ac.popoverPresentationController
    popover?.delegate = self
    popover?.permittedArrowDirections = .Any
    popover?.sourceView = self.view
    popover?.sourceRect = CGRect(x: 100, y: 100, width: 100, height: 100)

    presentViewController(ac, animated: true, completion: nil)
}

The UIViewController:

import UIKit

class EmptyViewController : UIViewController {
    override func viewDidLoad() {
        view.backgroundColor = UIColor.redColor()
    }
}

enter image description here

I am wondering how to make it a real popover (not full screen size). By the way, as @EI Captain indicated, it works perfectly on iPad but always fullscreen on iPhone.

4
in iPhone with portrait mode, with this code popover is always in full screen ... in iPad it works perfectly ... still check this richardallen.me/2014/11/28/popovers.htmlBhavin Bhadani
@EICaptain You are right that it works perfectly on iPad. However the tutorial is is working with IB. I am wondering how to make it work programmatically.Zizheng Wu
may be you need to make custom view or use any library for thisBhavin Bhadani
may be this is helpful to you ... github.com/corin8823/PopoverBhavin Bhadani

4 Answers

8
votes

For those who are looking how to do without segue 👇

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .systemBackground
        
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setImage(UIImage(systemName: "square.grid.2x2.fill"), for: .normal)
        button.addTarget(self, action: #selector(displayPopover), for: .touchUpInside)
        self.view.addSubview(button)
        
        NSLayoutConstraint.activate([
            button.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 100),
            button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
            button.widthAnchor.constraint(equalToConstant: 40),
            button.heightAnchor.constraint(equalToConstant: 40),
        ])
        
    }
    
    @IBAction func displayPopover(sender: UIButton!) {
        let popoverVC = PopoverViewController()
        popoverVC.modalPresentationStyle = .popover
        popoverVC.popoverPresentationController?.sourceView = sender
        popoverVC.popoverPresentationController?.permittedArrowDirections = .up
        popoverVC.popoverPresentationController?.delegate = self
        self.present(popoverVC, animated: true, completion: nil)
    }
}

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

class PopoverViewController: UIViewController {
    override func viewDidLoad() {
        self.view.backgroundColor = .systemGray
        self.preferredContentSize = CGSize(width: 300, height: 200)
    }
}

Result on iPhone 11 Pro Max 👇

enter image description here

6
votes

It is possible! I'm not sure since when though...

In the viewDidLoad of your UIViewController that is displayed as a popover:

    self.preferredContentSize = CGSize(width: myWidth, height: myHeight)

In the UIViewController that displays the popover, set the class as a UIPopoverPresentationControllerDelegate and:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    if segue.identifier == "Identifier" {
        let vc: UIViewController = segue.destination

        let pvc = vc.popoverPresentationController
        pvc?.delegate = self
        return
    }

}

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

PS: In the storyboard, don't forget to set the segue kind as "Present As Popover"

That's it!

0
votes

You can't do this in iPhone with portrait mode with this code ... you can check popover section in apple doc here..

It suggests that:

In iOS 8 and later, you use a UIPopoverPresentationController to present a popover. UIPopoverPresentationController defines a delegate that lets you adjust the display style of your popover content to suit the current display environment. For example, in a horizontally regular environment, your content can display inside a popover; in a horizontally compact environment, your content can display in a full-screen modal view.

And as I said, if you can check in iPad, your content can display inside a popover.

0
votes

You just need to implement the next method of the UIPopoverPresentationControllerDelegate protocol:

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