Fred A. Fred A. - 7 months ago 159
Swift Question

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!

Answer

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.