Фактическая разница между UIAccessibilityLayoutChangedNotification и UIAccessibilityScreenChangedNotification?

Я пытаюсь выяснить, что именно происходит по-разному при публикации UIAccessibilityLayoutChangedNotification и UIAccessibilityScreenChangedNotification. Из того, что я вижу, я могу использовать их взаимозаменяемо везде, и ничего другого не происходит.

В документации Apple просто говорится использовать LayoutChanged, когда (например) элемент был скрыт или показан, и использовать ScreenChanged, если меняется весь экран, но меня интересует, что ОНИ делают, когда я предоставляю эту информацию, и что я должен видеть по-разному при использовании того или другого.

Может ли кто-нибудь дать четкое объяснение различий в реализации между ними?


person Luke    schedule 06.01.2015    source источник


Ответы (2)


Эти два уведомления предназначены для динамического содержимого в представлениях и для передачи этих изменений в VoiceOver для пользователей программ чтения с экрана. Между этими двумя уведомлениями нет большой разницы, за исключением их поведения по умолчанию и глупого маленького «гудка» для уведомлений об изменении экрана.

В обоих случаях аргумент

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, arg);

Представляет строку для чтения или элемент на экране, на который VoiceOver переместит фокус. В случае резкого изменения контекста важно направить внимание на то место, которое имеет смысл, или объявить, что такие изменения произошли. Любой подход приемлем с точки зрения доступности, хотя я предпочитаю подходы, предполагающие наименьшее количество возможных изменений. В случае простых изменений макета почти всегда лучше просто объявить об изменении контекста и оставить фокус там, где он был. Хотя иногда элемент, вызвавший изменение контекста, скрывается, и тогда явно необходимо направлять озвучку для выделения нового контента, потому что поведение по умолчанию в этом случае не определено, а может, и детерминировано, но определяется фреймворком, который абсолютно ничего не знает. о вашем приложении!

Разница между двумя событиями, учитывая, что они оба делают одно и то же, заключается в их поведении по умолчанию. Если вы поставите nil в UIAccessibilityLayoutChangedNotification, это как если бы вы ничего не сделали. Если вы укажете нулевой аргумент для UIAccessibilityScreenChangedNotification, он отправит фокус на первый UIObject в вашей иерархии представлений, который помечен как accessibilityElement, как только все изменения иерархии представлений и рисунки будут завершены.

UIAccessibilityLayoutChangedNotification

Хороший пример использования UIAccessibilityLayoutChangedNotification для динамических форм. Вы хотите, чтобы пользователи знали, что в зависимости от решений, которые они приняли в форме, доступны новые параметры. Например, если в форме вы укажете, что являетесь ветераном, могут появиться дополнительные области формы для ввода дополнительных данных, но эти области могут быть скрыты для других пользователей, которым они не нужны. Таким образом, вы можете сместить фокус на эти элементы после взаимодействия с пользователем:

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, firstNewFormElement);

Что сместит фокус на предоставленный элемент и объявит его accessibilityLabel.

Или просто скажите им, что появились новые элементы формы:

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, @"Veterans form elements available");

Это оставило бы фокус там, где он есть, но VoiceOver объявил бы «Доступны элементы формы ветеранов».

Примечание. Это конкретное поведение вызывает ошибку на моем iPad (8.1.2).

Или, наконец, вы могли бы сделать это:

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);

Который абсолютно ничего не делает :). Серьезно, я даже не думаю, что бэкэнд a11y framework заботится. Эта конкретная строка кода — полная трата!

UIAccessibilityScreenChangedNotification

Хорошим примером использования UIAccessibilityScreenChangedNotification являются настраиваемые ситуации просмотра вкладок. Когда меняется весь экран, за исключением области навигации. Вы хотите, чтобы голос за кадром знал, что по существу изменился весь экран, но НЕ для того, чтобы сфокусироваться на первом элементе (ваша первая вкладка), а чтобы сфокусироваться на первом элементе контента.

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, firstNonGlobalNavElement);

Который воспроизведет звук «бип», а затем переместит фокус прямо под вашу глобальную панель навигации. Или вы можете сделать это:

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, @"You're on a new tab");

Который будет ждать загрузки новой вкладки, воспроизводить звук «бип-буп», объявлять «Вы находитесь на новой вкладке» в голосе за кадром, затем перемещать фокус на первый элемент на экране, а затем объявлять accessibilityLabel для этого элемента. (ФУУУ! Это много! Это раздражает пользователей программ чтения с экрана. Избегайте этого сценария, если в этом нет крайней необходимости).

И, наконец, вы, конечно, можете сделать это:

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);    

Что эквивалентно:

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, firstA11yElement);

Оба из них будут воспроизводить звук «бип-буп», переключать фокус VoiceOver на первый элемент на экране, а затем объявлять об этом.

Наконец-то

В комментарии кто-то упомянул кэширование, и я иногда комментирую в своем ответе вещи, которые могут или не могут быть важны для A11y Backend. Хотя вполне возможно, что происходит какое-то волшебство бэкэнда, я не верю ни в одно из этих обстоятельств, бэкенду вообще все равно. Я говорю это потому, что:

Если вы когда-либо использовали протокол UIAccessibilityContainer, вы можете наблюдать, как запрашивается ваш контейнер представлений. Кэширование не происходит. Даже свойство accessibilityElementCount проверяется каждый раз, когда VoiceOver изменяет фокус на новый AccessibilityElement в вашем контейнере. Затем он проходит процесс проверки того, на каком элементе он находится, запрашивает следующий элемент и так далее. Он разработан по своей сути для обработки динамических ситуаций. Если бы вы вставили новый элемент в свой контейнер после взаимодействия, он все равно прошел бы все эти запросы и был бы в порядке! Кроме того, если вы переопределите свойства протокола UIAccessibility для предоставления динамических подсказок и меток, вы также увидите, что эти функции вызываются каждый раз! Таким образом, я считаю, что серверная часть A11y Framework собирает АБСОЛЮТНО НУЛЕВУЮ информацию из этих уведомлений. Единственная информация, необходимая VoiceOver для выполнения своей работы, — это элемент специальных возможностей, сфокусированный в данный момент, и контейнер специальных возможностей этого элемента. Уведомления просто нужны вам, чтобы сделать ваше приложение более удобным для пользователей VoiceOver.

Представьте, если бы это было не так, сколько раз Safari публиковал бы эти уведомления!!!! :)

Эти конкретные утверждения могут быть подтверждены только кем-то, кто хорошо разбирается в инфраструктуре, кто работает с кодом, и их следует рассматривать как предположения. Возможно, это сильно зависит от версии/реализации. Определенно открыт для обсуждения этих вопросов! Остальная часть этого поста довольно конкретна.

Для справки

Большая часть этого исходит из опыта работы с фреймворками, но вот полезная ссылка, если вы хотите копнуть дальше.

https://developer.apple.com/documentation/uikit/accessibility/uiaccessibility

https://developer.apple.com/documentation/uikit/uiaccessibilitylayoutchangednotification

https://developer.apple.com/documentation/uikit/uiaccessibilityscreenchangednotification

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

https://github.com/chriscm2006/IOS-A11y-Api-Test

person ChrisCM    schedule 05.02.2015
comment
Успешно справился. Хорошая идея проверить против UIAccessibilityContainer. - person Justin; 06.02.2015
comment
Идеально. Спасибо. Я согласен с @Justin. Я также воспроизвел ошибку, о которой вы упомянули, и отправил Radar. - person Aaron Brager; 06.02.2015
comment
Отличный ответ. Я лишь немного не согласен насчет кэширования. Я на 100% уверен, что где-то есть какой-то кеш. Объяснение: я провел несколько тестов с UIAccessibilityContainer, и, как вы сказали, методы контейнера вызываются при перемещении фокуса. НО, какие бы методы протокола контейнера ни возвращались, в некоторых ситуациях закадровый голос продолжает читать неправильные значения, если вы явно не публикуете уведомление об изменении макета/экрана. Так что есть кэширование под капотом. Он может кэшировать не только элементы, но кэширует пары (элемент, позиция) или что-то вокруг этих строк. - person Aurelien Porte; 11.11.2015
comment
Было бы здорово воспроизвести пример и опубликовать суть или что-то в этом роде. - person ChrisCM; 11.11.2015
comment
@ChrisCM Руководство Apple по программированию специальных возможностей для iOS включает фрагмент кода, который публикует уведомление об изменении макета с параметром nil. - person Justin; 01.03.2016
comment
Этот ответ действителен на момент его написания (iOS 8.0 ish). Со времен iOS 9 все могло измениться. Хотя я считаю, что этот код, скорее всего, написал стажер, который не понимает API специальных возможностей. Вариант использования немного уникален тем, что это клавиатура. Я мог бы проверить это на себе как-нибудь. - person ChrisCM; 01.03.2016

UIAccessibilityScreenChangedNotification означает, что весь экран изменился и необходимо сбросить VoiceOver.

UIAccessibilityLayoutChangedNotification означает, что один или несколько, но не все, элементы на экране изменились.

когда ваш пользовательский интерфейс резко меняется. Обычно, когда пользователь переходит в другую часть вашего приложения (переходит на другой экран). VoiceOver уведомляет пользователя звуковым сигналом, очищает свои кэши и выполняет другие приготовления для работы с новым набором данных доступности. Он уведомляет VoiceOver о том, что экран изменился и на нем могут появиться новые элементы, поэтому VoiceOver перестроит свой индекс элементов доступности.

UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil);

Если какая-то часть вашего пользовательского интерфейса изменилась, но пользователь не обязательно перешел в совершенно другую часть вашего приложения. (Пример: в приложении iTunes Store нажатие ценника (0,99 доллара США и т. д.) рядом с песней меняет его на кнопку «Купить».) находятся на экране, и, делая это, он выясняет, что изменилось, и информирует пользователя об этих изменениях. Он уведомляет VoiceOver об изменении макета и о том, что его текущий индекс устарел, поскольку элементы на экране изменили свой порядок.

UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
person Dayton Wang    schedule 04.02.2015
comment
Это честные примеры, но они не отвечают на вопрос. Можете ли вы предоставить доказательства в поддержку того, что A) VoiceOver очищает свои кэши в ответ на уведомление об изменении экрана, но не уведомление об изменении макета, и что B) VoiceOver уведомляет пользователя звуковым сигналом в ответ на изменение экрана, но не изменения макета ? - person Justin; 05.02.2015