При использовании Voice Over в iOS вызов UIAccessibility.post(notification:argument:)
для объявления ошибки поля фактически не объявляет ошибку.
У меня есть кнопка отправки, и при ее фокусировке голос за кадром читает заголовок кнопки, как и следовало ожидать. При нажатии кнопки голос за кадром снова читает заголовок. Когда кнопка отправки нажата, я выполняю некоторую проверку и, когда возникает ошибка поля, я пытаюсь объявить об этом, вызывая:
if UIAccessibility.isVoiceOverRunning {
UIAccessibility.post(notification: .announcement, argument: "my field error")
}
Интересно, что если я остановлюсь на точке останова в отладчике, произойдет объявление. Когда я не останавливаюсь на точке останова, объявление не происходит.
Уведомление публикуется в основной ветке, и, если оно похоже на NotificationCenter.default
, я предполагаю, что оно обрабатывается в той же ветке, в которой оно было размещено. Я попытался отправить вызов в основную очередь, даже если он уже находится в главном потоке, и это тоже не работает.
Единственное, о чем я могу подумать, это то, что уведомление публикуется и наблюдается до того, как голос за кадром закончится, после прочтения заголовка кнопки отправки, и уведомление об объявлении не прервет текущий голос.
Буду очень признателен за любую помощь в этом.
Обновление
Я могу заставить это работать, используя механизм повтора, где я регистрируюсь как наблюдатель UIAccessibility.announcementDidFinishNotification
, а затем извлекаю объявление и статус успеха из словаря userInfo
.
Если статус успеха равен false и объявление совпадает с тем, которое я только что отправил, я снова отправляю уведомление. Это повторяется до тех пор, пока объявление не будет успешным.
Очевидно, что при таком подходе существует множество проблем, в том числе необходимость отменить регистрацию, что произойдет, если другому объекту удастся опубликовать такое же объявление (это не должно происходить на практике, но теоретически это может произойти), необходимость отслеживать последнее отправленное объявление и т. д.
Код будет выглядеть так:
private var _errors: [String] = []
private var _lastAnnouncement: String = ""
init() {
NotificationCenter.default.addObserver(
self,
selector: #selector(announcementFinished(_:)),
name: UIAccessibility.announcementDidFinishNotification,
object: nil
)
}
func showErrors() {
if !_errors.isEmpty {
view.errorLabel.text = _errors.first!
view.errorLabel.isHidden = false
if UIAccessibility.isVoiceOverRunning {
_lastAnnouncement = _errors.first!
UIAccessibility.post(notification: .announcement, argument: _errors.first!)
}
} else {
view.errorLabel.text = ""
view.errorLabel.isHidden = true
}
}
@objc func announcementFinished(_ sender: Notification) {
guard let announcement = sender.userInfo![UIAccessibility.announcementStringValueUserInfoKey] as? String else { return }
guard let success = sender.userInfo![UIAccessibility.announcementWasSuccessfulUserInfoKey] as? Bool else { return }
if !success && announcement == _lastAnnouncement {
_lastAnnouncement = _errors.first!
UIAccessibility.post(notification: .announcement, argument: _errors.first!)
}
}
Проблема в том, что этот механизм повтора всегда будет использоваться, потому что всегда первый вызов UIAccessibility.post(notification: .announcement, argument: _errors.first!)
(если меня не остановили на точке останова). Я до сих пор не знаю, почему первый пост всегда терпит неудачу.