-1

I'm attempting to display one UIViewController as a popover from another. For that, I've established the following...

func showPopover(ofViewController popoverViewController: UIViewController, sender: UIView) {
    popoverViewController.modalPresentationStyle = .popover
    popoverViewController.popoverPresentationController?.sourceView = sender
    popoverViewController.popoverPresentationController?.sourceRect = sender.bounds
    popoverViewController.popoverPresentationController?.delegate = self
    self.present(popoverViewController, animated: true, completion: nil)
}

However, the new VC always shows as a full-screen, modal presentation on compact devices, rather than an actual popover. Based on what I've read here & here, that's normal behaviour, but should be customizable through delegation.

I've declared the presenting VC as implementing UIPopoverPresentationControllerDelegate, set it as the delegate, and implemented the required methods; however, the delegation methods are never getting called. This means the 'popover' is still getting shown modally, regardless.

Any advice would be welcome.

Some other callouts:

  • The viewControllerForAdaptivePresentationStyle does get called if an @objc marker is added before it, but that doesn't work for the others.
  • Xcode is giving a warning for each one: Instance method ... nearly matches optional requirement ... of protocol 'UIAdaptivePresentationControllerDelegate'; however, the method signature is a 100% match. Not sure if this is an instance of this bug, which some say still exists in Xcode 10.1.

Thanks.

Delegate functions implemented:

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

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

func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
    switch style {
    case .fullScreen: // Configuration for full-screen
    default: return controller.presentedViewController
    }
}
TheNeil
  • 3,321
  • 2
  • 27
  • 52
  • You are not setting the size of the popover view. – El Tomato Mar 17 '19 at 23:10
  • Thanks for the suggestion, but I don't believe that's something that's required for the basic functionality (it's not for the iPad configuration), and it doesn't change the fact that the adaptive presentation style is coming back as .fullScreen; however, I did try also putting the following into the presented VC, which didn't help: `override var preferredContentSize: CGSize { get { let size = CGSize(width: 80, height: 60) return size } set { super.preferredContentSize = newValue } }` – TheNeil Mar 17 '19 at 23:16
  • You need to resolve those warnings; I suspect that the functions aren't being called because of that. Returning `.none` from `adaptivePresentationStyle(for: traitCollection:)` is all that is required. It is odd, though, as I pasted your code into Xcode and I don't get those warnings. – Paulw11 Mar 17 '19 at 23:52
  • That's what I thought too, and I'm still open to advice on how that can be done; however, there's no way to resolve the warnings that I can see (the methods exactly match), and `viewControllerForAdaptivePresentationStyle` is being called, despite having such a warning too. – TheNeil Mar 17 '19 at 23:53
  • I wouldn't have expected them to be needed either, though it seems to be related to that Xcode bug (see here for details: https://stackoverflow.com/questions/39495773/xcode-8-warning-instance-method-nearly-matches-optional-requirement). Unfortunately, cleaning the build folder doesn't seem to make a difference, the warnings remain and `viewControllerForAdaptivePresentationStyle` doesn't get called without `@objc` – TheNeil Mar 18 '19 at 00:03
  • FYI, I'm not sure if this would make a difference, but I'm putting this in an extension to the regular UIViewController, rather than a subclass, so it can be used across all: `extension UIViewController: UIPopoverPresentationControllerDelegate` – TheNeil Mar 18 '19 at 00:04
  • Working example https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch09p477popoversOnPhone/PopoverOnPhone/ViewController.swift – matt Mar 18 '19 at 00:09
  • 1
    I wouldn't extend `UIViewController`; that could have unintended consequences – Paulw11 Mar 18 '19 at 00:36
  • Perhaps that's the issue. I will put together a shared subclass instead, and see if that helps. – TheNeil Mar 18 '19 at 00:45
  • I have just ignored the warning and after some time playing with the method declaration line, the warning disappeared. The code is called. – Vladimír Slavík Oct 28 '19 at 15:18

1 Answers1

1

Thanks to Paulw11 for confirming that the problem was caused by implementing the code in an extension of UIViewController. This was resulting in strange behaviours, whereby the code wasn't being called as usual.

When migrated to a shared subclass of UIViewController, the following problems were all resolved:

  • The adaptivePresentationStyle method never being called.
  • The viewControllerForAdaptivePresentationStyle method only being called if preceded by the @objc tag.
  • Xcode giving Instance method ... nearly matches optional requirement ... of protocol 'UIAdaptivePresentationControllerDelegate' errors.

Corrected code is as follows, for anyone seeking the same functionality.

class CustomViewController: UIViewController {

    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.backgroundColor = popoverViewController.view.backgroundColor
            popoverController.permittedArrowDirections = UIPopoverArrowDirection.any
        }
        self.present(popoverViewController, animated: true)
    }
}

extension CustomViewController: UIPopoverPresentationControllerDelegate {

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

    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: // Configuration for full-screen
        default:
            return controller.presentedViewController
        }
    }
}
TheNeil
  • 3,321
  • 2
  • 27
  • 52