UIBarButtonItem не обесцвечивается / отключается, когда всплывающее окно на экране

У меня два UIBarButtonItems на моем навигационном контроллере:

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

При нажатии infoItem Я делаю:

@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)
}

Это тогда призывает UIPopoverPresentationControllerDelegate функции:

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

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

Хотя я поставил passthroughViews в nil дважды UISegmentedControl не обесцвечивается и остается всплывающим, пока всплывающее окно находится на экране.

Если показывать любой другой поповер UISegmentedControl ведет себя нормально: обесцвечивается и не наносится.

Что мне здесь не хватает?

2 ответа

Глядя на ваш код, кажется, все в порядке. Кажется, есть ошибка в ОС.

Я нашел быстрое решение для этого, если они не проверят и не исправят это в следующем выпуске iOS.

  1. Определите и barButtonItems и переменную, чтобы сохранить существующий цвет оттенка глобально в вашем ViewController.

    var infoItem: UIBarButtonItem!
    var segmentItem: UIBarButtonItem!
    var savedTintColour: UIColor? = nil
    
  2. В вашем ViewDidLoad() Инициализируйте их

    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. Код для InfoAction останется прежним.

    @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. Реализуйте метод делегата prepareForPopoverPresentation и установите цвет оттенка на darkGray и сохраните ранее доступный tintColour в переменную, чтобы мы могли использовать его при включении.

    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. Реализуйте метод делегата popoverPresentationControllerDidDismissPopover, чтобы сбросить цвет вашего сегмента Control и включить segmentedItem.

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

Надеюсь, поможет.

Как предполагает Бхавин Кансагара, имитация поведения iOS - это верный обходной путь. Его ответ был близок, но упустил несколько деталей:

  • popoverPresentationControllerDidDismissPopover вызывается слишком поздно, в результате сегментированный элемент управления снова становится синим после всех других элементов пользовательского интерфейса. Нужно использовать popoverPresentationControllerShouldDismissPopover вместо.
  • Изменения цвета должны быть анимированными, как в iOS.
  • segmentedControl "s isEnabled также должны быть сохранены.
  • Обрабатывать светлый цвет в отключенном состоянии.

Вот что я сделал, надеясь на лучшее решение:

private var segmentedControlTintColor: UIColor?
private var segmentedControlIsEnabled: Bool = true

// Due to, what seems to be, an iOS issue, the segmented control is not decolorized when the info popover is
// on screen.  The two functions below mimick iOS behavior until a better solution is found.
func decolorizeSegmentedControl()
{
    segmentedControlIsEnabled = segmentedControl.isEnabled
    segmentedControl.isEnabled = false
    segmentedControlTintColor = segmentedControl.tintColor
    UIView.animate(withDuration: 0.333)
    {
        self.segmentedControl.tintColor = self.segmentedControlIsEnabled ? .darkGray : .lightGray
    }
}

func colorizeSegmentedControl()
{
    segmentedControl.isEnabled = segmentedControlIsEnabled
    UIView.animate(withDuration: 0.333)
    {
        self.segmentedControl.tintColor = self.segmentedControlTintColor
    }
}

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

    decolorizeSegmentedControl()
}

func popoverPresentationControllerShouldDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) -> Bool
{
    colorizeSegmentedControl()

    return true
}
Другие вопросы по тегам