Popover presentation style on iPhone devices - possible any more?
Asked Answered
S

2

8

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+.

Semiweekly answered 24/7, 2017 at 16:8 Comment(0)
S
5

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

Shoup answered 28/7, 2017 at 2:19 Comment(6)
This works great. I confess that when I first looked at it, I thought it was just a reorganization of the same code that I had tried and that it would work no differently. But that's clearly not the case. Can you explain something about why this works when my code didn't?Semiweekly
I can see the issue on your preferredContentSize where you are setting the frame but not updating the value inside super.preferredContentSize. So in my code I am also setting super.preferredContentSize = newValue. That could be the cause on your case. Is that make sense?Shoup
I see how that might be an issue that affects the size. But my version doesn't even come up with a popover style in many cases. It's not just the size that's wrong. I'd like to understand why my version fails and yours works fine. Could setting the size (or failing to do so) cause the entire style to change?Semiweekly
I can see your point but so far I can only see the difference of setting size in its super. preferredContentSize. Apart from that I do not see any issue on your code.Shoup
Weird. Maybe I'll play around with this a little more in the hopes of gaining a better insight. In any case, you solved my problem, so many thanks!Semiweekly
Np, I am glad it helps you:)Shoup
B
1

iOS 15 has some new ways to solve this problem. Take a look at the WWDC21 Session "Customize and Resize Sheets in UIKit" https://developer.apple.com/wwdc21/10063 Pretty simple new interface for popovers and customized sheets. Shows how to do non-modal interaction with pop over and the view behind it.

Berkley answered 3/2, 2022 at 23:34 Comment(1)
Sounds like a big improvement. I'll take a look at the material. Thanks!Semiweekly

© 2022 - 2025 — McMap. All rights reserved.