Почему UIAccessibilityNotification не переходит к правильному аргументу? - PullRequest
1 голос
/ 31 января 2020

У меня есть UISwitch внутри ячеек таблицы. Переключатель делает сетевой запрос. A UIActivityIndicator заменяет коммутатор, пока ответ не завершится. Таким образом, поток UX выглядит следующим образом:

off --> loader --> on

До того, как метка будет нажата, доступность выглядит следующим образом:

label - value - hint
‘streaming - switch button off - double tap to toggle switch’

Мой код таков:

@IBAction func switchToggled(_ sender: Any) {
    toggle.isHidden = true
    activityIndicatorView.startAnimating()
    UIAccessibility.post(notification: .layoutChanged, argument: activityIndicatorView)

Я также отключил доступность самой ячейки.

Проблема заключается в том, что доступность будет читаться так:

$switchValue - activityIndicatorLabel-activityIndicatorValue - switchLabel switchValue
off - streaming - in progress - streaming - switch button on

Я не уверен, почему accessibility по-прежнему читает после того, как я установил переключатель isHidden на true, а также опубликовал уведомление о доступности ?‍♂️

Я хочу пропустить off сверху ☝️ и сделать так, чтобы он прочитал например:

$activityIndicatorLabel-activityIndicatorValue - switchLabel switchValue
streaming - in progress - streaming - switch button on

FWIW

UIAccessibility.post(notification: .screenChange, argument: activityIndicatorView)

Получает требуемую последовательность голоса, но затем создает новую проблему. То есть я получаю звук 'b oop beep', и это может ввести пользователя в заблуждение, будто он находится на новом экране.

Довольно странно, что это уведомление перемещается на нужный элемент, тогда как другое не делает. Я ожидал, что оба создадут одинаковый порядок, но они не


РЕДАКТИРОВАТЬ:

Я уже изучил Как остановить Текст в речь, когда голос за кадром говорит, или наоборот в Swift?

и я наблюдаю уведомление UIAccessibilityElementFocusedNotification. Я получаю три предмета в userInfo.

▿ Optional<Dictionary<AnyHashable, Any>>
  ▿ some : 3 elements
    ▿ 0 : 2 elements
      ▿ key : AnyHashable("UIAccessibilityFocusedElementKey")
        - value : "UIAccessibilityFocusedElementKey"
      - value : <UIActivityIndicatorView: 0x113b59350; frame = (794 20; 24 24); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x2811a91e0>>
    ▿ 1 : 2 elements
      ▿ key : AnyHashable("UIAccessibilityAssistiveTechnologyKey")
        - value : "UIAccessibilityAssistiveTechnologyKey"
      - value : UIAccessibilityNotificationVoiceOverIdentifier
    ▿ 2 : 2 elements
      ▿ key : AnyHashable("UIAccessibilityUnfocusedElementKey")
        - value : "UIAccessibilityUnfocusedElementKey"
      - value : <MyModule.MySwitch: 0x113b59540; baseClass = UISwitch; frame = (769 16.5; 51 31); hidden = YES; opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x2811a9120>>

Есть две вещи, которые я не знаю, как решить.

  1. Три события происходят не по порядку. Это всего лишь одно уведомление, содержащее все три. Я хочу отменить VoiceOver для коммутатора не сфокусировано. Я не хочу, чтобы это читали.
  2. Ответ до сих пор не объясняет, как отменить нежелательный голос за кадром.

1 Ответ

0 голосов
/ 07 февраля 2020

К сожалению, Voice Over имеет много ошибок (в начале своего существования он был намного лучше, но с изменениями в последующих iOS версиях он ухудшился). Заставить его вести себя по-другому - это много "взломать", и с новой системой он может снова вести себя так, как вы этого не сделаете. Сказав, что я представлю решение, которое можно использовать, по крайней мере, на данный момент.

При запуске я должен сказать, что приостановка VoiceOver не возможна программно, потому что это вызывает cra sh ( проверьте здесь ). И поэтому

UIAccessibility.post(notification: .pauseAssistiveTechnology, argument: UIAccessibility.AssistiveTechnologyIdentifier.notificationVoiceOver)

не поможет.

Лучший способ обработки UITableViewCell с помощью UISwitch - это сделать так же, как Apple, в настройках iPhone (фокусировка Voice Over on целая клетка). Таким образом, ваш переключатель не должен быть элементом доступности.

А теперь самое забавное, что нужно обнаружить - если флаг switch.isUserInteractionEnabled установлен в значение true, то двойное нажатие на ячейку не вызовет

tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath)

или

tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)

, поэтому система будет go по умолчанию и будет считывать новое значение ползунка сразу после его изменения.

Но если вы создадите метод для установки новой ячейки, снятой с очереди (( внутри вашего пользовательского класса ячеек) и напишите что-то вроде этого

func setup() {
    slider.isUserInteractionEnabled = !UIAccessibility.isVoiceOverRunning //if VO is running you can't normally tap slider so it won't affect user experience
    NotificationCenter.default.removeObserver(self)
    NotificationCenter.default.addObserver(self, selector: #selector(setSwitchAccessibility), name: UIAccessibility.voiceOverStatusDidChangeNotification, object: nil)
}

@objc func setSwitchAccessibility() {
    slider.isUserInteractionEnabled = !UIAccessibility.isVoiceOverRunning //without VO user should be able to interact with switch directly or / and by selecting cell
}

, тогда будет вызван ваш делегат таблицы, позволяющий сделать что-то свое Наблюдение за передачей голоса необходимо для того, чтобы разрешить обработку переключения при отключении Voice Over.

Под «чем-то нестандартным» я понимаю отображение индикатора активности в willSelectRow и перемещение фокуса на

UIAccessibility.post(notification: .layoutChanged, argument: activityIndicatorView) 

и настройку значение доступности, равное nil (установите его снова в значение доступности ползунка непосредственно перед восстановлением фокуса Voice Over на ячейке).

Также - после удаления значения доступности из ячейки Voice Over прочитает метку доступности этой ячейки, если она существует. В этом случае вам нужно временно перезаписать значение по умолчанию accessibilityLabel с помощью настраиваемого свойства этой ячейки (значение nil), чтобы сделать его беззвучным, используя что-то вроде этого:

var labelToReturn: String? = ""

    override func accessibilityActivate() -> Bool {
        self.accessibilityValue = nil
        labelToReturn = nil
        return false
    }

    override var accessibilityLabel: String? {
        get {return labelToReturn == nil ? labelToReturn: super.accessibilityLabel}
        set {super.accessibilityLabel = newValue}
    }

Не забудьте изменить labelToReturn на что-то другое перед удалением сфокусироваться на индикаторе вашей активности.

...