Search code examples
iosiphoneswift3popover

Popover presentation style on iPhone devices - possible any more?


I'm trying to define a popover view attached to a view like this:

desired popover appearance

Here's my code:

class MyController: UIViewController, UIPopoverPresentationControllerDelegate {

    ...

    func displaySignOut(_ sender: UIButton) {
        let vc = UIStoryboard(name: "Main", bundle: nil)
            .instantiateViewController(withIdentifier: "signOutPopover")
        vc.modalPresentationStyle = .popover
        vc.preferredContentSize = CGSize(width: 100, height: 30)
        present(vc, animated: true, completion: nil)

        let pc = vc.popoverPresentationController!
        pc.sourceView = sender
        pc.sourceRect = sender.bounds
        pc.delegate = self
    }

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

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

Because the popover is so small, I'd like to use this style on all devices. I've followed the usual advice (e.g., here) on overriding adaptivePresentationStyle to return UIModalPresentationStyle.none.

This works fine on iPad devices, but on iPhones, it doesn't. On smaller iPhone devices, it comes up full screen all the time. On larger screens (e.g., iPhone 7 Plus), it comes up wrong, but, weirdly, switches to a popover presentation (in both portrait and landscape) if I rotate the device after the popover appears. (If I dismiss the popover and bring it up again, it's wrong again until I rotate the device.) Furthermore, in landscape it comes up in a strange configuration (not full screen as in portrait):

How the popover appears in landscape on iPhone 7 Plus

Unlike with a popover presentation, this does not dismiss if I tap outside the popover view itself.

The Apple documentation says (in part):

In a horizontally compact environment, popovers adapt to the UIModalPresentationOverFullScreen presentation style by default.

The "by default" strongly suggests that there's a way to override this behavior. But (as is consistent with this post), overriding adaptivePresentationStyle in the delegate doesn't seem to be the way to do this any more (although it used to work). So is there a new way to modify the default behavior?

I'm using XCode 8.3.3 and Swift 3.1, targeting iOS 9+.


Solution

  • I have created one custom class with storyboard inside that connect outlet of button and implemented below code.

    import UIKit
    class PopOverViewController: UIViewController {
    
        @IBOutlet weak var button: UIButton!
        override func viewDidLoad() {
            super.viewDidLoad()
            button.backgroundColor = UIColor.purple
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
        //Updating the popover size
        override var preferredContentSize: CGSize {
            get {
                let size = CGSize(width: 80, height: 60)
                return size
            }
            set {
                super.preferredContentSize = newValue
            }
        }
    
    
        //Setup the ViewController for popover presentation
        func updatePopOverViewController(_ button: UIButton?, with delegate: AnyObject?) {
            guard let button = button else { return }
            modalPresentationStyle = .popover
            popoverPresentationController?.permittedArrowDirections = [.any]
            popoverPresentationController?.backgroundColor = UIColor.purple
            popoverPresentationController?.sourceView = button
            popoverPresentationController?.sourceRect = button.bounds
            popoverPresentationController?.delegate = delegate
        }
    
    }
    

    And then Inside ViewController implemented one function to show popOver on iphone

    func showPopOver(button: UIButton!) {
     let viewController = PopOverViewController()
     viewController.updatePopOverViewController(button, with: self)
      present(viewController, animated: true, completion: nil)
    }
    

    Note:- Tested and this should work on Portrait as well Landscape mode