Zizheng Wu
Zizheng Wu

Reputation: 676

How to present a UIViewController as a popover in Swift programmatically on iPhone

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.

Upvotes: 6

Views: 14754

Answers (4)

Ira
Ira

Reputation: 1

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

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

Upvotes: 0

S.Basnagoda
S.Basnagoda

Reputation: 721

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

Upvotes: 15

lorenzo
lorenzo

Reputation: 1743

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!

Upvotes: 6

Bhavin Bhadani
Bhavin Bhadani

Reputation: 22374

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.

Upvotes: 0

Related Questions