Search code examples
ipadswiftios8xcode6uipopover

iOS 8 : Disable UIBarButtonItem when shown inside a popover


Now that XCode 6 and iOS 8 enable size classes, there is only one storyboard for all devices. Fine. But in the same time, Apple recommends to avoid using bar button items to dismiss a popover. Now, how can I do to have a UIBarButtonItem when presenting on an iPhone, and not when presented inside a popover on an iPad ?

Precision : I know how to disable a UIBarButtonItem based on the fact that the device is an iPad. I'm looking for a way to specifically detect that a popover is displayed.

EDIT: this code works, but I'd like something less device-dependent:

if traitCollection.userInterfaceIdiom == .Pad {
        navigationItem.rightBarButtonItem = nil
    }

EDIT: I created a small Github project to make my question clear : Github - Test Popover

Thanks!


Solution

  • I finally found how to solve that issue :

    • In the storyboard, I removed the "close" bar button item in the navigation bar of the view controller I want to display as a popover on iPad and a modal VC on iPhone. So now this controller is embedded in a navigation controller, but without a "close" button. enter image description here
    • In the caller view controller (the one which has a bar button item to call the popover), I added the following code in the prepareForSegue (my segue is a Popover segue by the way)

    PrepareForSegue:

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
    
    // switch on the segue.identifier
    // ....
           case .DisplayPreferences:
               print("Segue to Preferences")
               if let popoverPresentationController = segue.destinationViewController.popoverPresentationController {
                 popoverPresentationController.delegate = self
               }
    // ....
    }
    

    • I added the following extension so that the caller view controler conforms to UIPopoverPresentationControllerDelegate protocol

    UIPopoverPresentationControllerDelegate:

    // MARK: - UIPopoverPresentationController Delegate methods
    extension StockListTableViewController: UIPopoverPresentationControllerDelegate {
    
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
        return .FullScreen
    }
    
    
    func presentationController(controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        if let navigationVC = controller.presentedViewController as? UINavigationController,
            let preferencesVC = navigationVC.visibleViewController as? PreferencesTableViewController {
            // This Bool indicates whether the popover controller should display the bar button item or not
            preferencesVC.shouldShowCloseButton = true
        }
        return controller.presentedViewController
    }
    

    }


    • Finally, in the called view controller (which is a popover on iPad but modal on iPhone), I added the following code:

    property:

        var shouldShowCloseButton = false
    

    viewDidLoad:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        // Close button
        if shouldShowCloseButton {
            let closeButton = UIBarButtonItem(barButtonSystemItem: .Done, target: self, action: #selector(PreferencesTableViewController.doneButtonTapped(_:)))
            navigationItem.setRightBarButtonItem(closeButton, animated: true)
        }
    }
    

    doneButtonTapped:

    func doneButtonTapped(sender: UIBarButtonItem) {
        navigationController?.dismissViewControllerAnimated(true, completion: nil)
    }
    

    For me, this worked perfectly:

    • on iPhone, I get a view controller presented modally, with a "close" button item in a navigation controller, which allows me to dismiss it

    • on iPad, I get a popover without a "close" button because with a popover th expected behavior is to tap outside the popover to dismiss it.