Search code examples
iosswiftuipopovercontrolleruipopoverpresentationcontroller

UIBarButtonItem not decolorized/disabled when popover is on screen


I have two UIBarButtonItems on my navigation controller:

    segmentControl = UISegmentedControl(items: ["Up", "Down"])
    infoItem = UIBarButtonItem(image: infoImage,
                               style: .plain, 
                               target: self,
                               action: #selector(infoAction))
    navigationItem.rightBarButtonItems = [infoItem, UIBarButtonItem(customView: segmentControl)]

When tapping infoItem I do:

@objc func infoAction()
{
    let popoverContentController = InfoViewController()

    popoverContentController.preferredContentSize = CGSize(width: 300, height: 300)
    popoverContentController.modalPresentationStyle = .popover
    popoverContentController.popoverPresentationController?.delegate = self
    popoverContentController.popoverPresentationController?.passthroughViews = nil

    self.present(popoverContentController, animated: true, completion: nil)
}

This then calls out to UIPopoverPresentationControllerDelegate functions:

func prepareForPopoverPresentation(_ popoverPresentationController: UIPopoverPresentationController)
{
    popoverPresentationController.permittedArrowDirections = .any
    popoverPresentationController.barButtonItem = infoItem
    popoverPresentationController.passthroughViews = nil
}

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

Even though I set passthroughViews to nil twice, the UISegmentedControl is not decolorized and remains tappable while the popover is on screen.

If showing any other popover the UISegmentedControl behaves normally: decolorized and not tappable.

What am I missing here?


Solution

  • Looking at your code, everything seems to be fine. It seems there is a bug in OS.

    I have found a quick fix for this, unless they check and fix it in next release of iOS.

    1. Define both barButtonItems and a variable to save the existing tint colour globally in your ViewController.

      var infoItem: UIBarButtonItem!
      var segmentItem: UIBarButtonItem!
      var savedTintColour: UIColor? = nil
      
    2. In your ViewDidLoad() Initialize them

      segmentedControl = UISegmentedControl(items: ["Up", "Down"])
      infoItem = UIBarButtonItem(image: UIImage(named: "setting_mobile"),
                                 style: .plain,
                                 target: self,
                                 action: #selector(infoAction))
      segmentItem = UIBarButtonItem(customView: segmentedControl)
      navigationItem.rightBarButtonItems = [infoItem, segmentItem]
      
    3. The code for InfoAction will be remain the same.

      @objc func infoAction() {
          let popoverContentController = InfoViewController()
      
          popoverContentController.preferredContentSize = CGSize(width: 300, height: 300)
          popoverContentController.modalPresentationStyle = .popover
          popoverContentController.popoverPresentationController?.delegate = self
          popoverContentController.popoverPresentationController?.passthroughViews = nil
      
          self.present(popoverContentController, animated: true, completion: nil)
      }
      
    4. Implement the delegate method prepareForPopoverPresentation and set the tint colour to darkGray and save the previously available tintColour to a variable so that we can reuse that while enabling.

      func prepareForPopoverPresentation(_ popoverPresentationController: UIPopoverPresentationController) {
      popoverPresentationController.permittedArrowDirections = .any
      popoverPresentationController.barButtonItem = infoItem
      popoverPresentationController.passthroughViews = nil
      
      self.segmentItem.isEnabled = false
      if savedTintColour == nil {
          savedTintColour = self.segmentedControl.tintColor
      }
      self.segmentedControl.tintColor = .darkGray
      }
      
    5. Implement a delegate method popoverPresentationControllerDidDismissPopover, to reset the colour of your segmentControl and Enable the segmentedItem.

      func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
      self.segmentItem.isEnabled = true
      self.segmentedControl.tintColor = savedTintColour!
      }
      

    Hope it helps.