- PVSM.RU - https://www.pvsm.ru -
Привет! Недавно я говорил про адаптацию приложений для незрячих и неподвижных людей. И не договорил!
Сегодня расскажу, как изменить поведение контролов с помощью accessibilityTraits и сделать жизнь незрячих чуть удобней. Знать работу этих трейтов (traits) важно, чтобы не писать свои костыли.

В первой части [1] мы начали разбираться с адаптацией приложений для незрячих с помощью VoiceOver: подписали контролы, сгруппировали их, исправили навигацию. В этой статье пойдём дальше и рассмотрим «особенности», которые можно дать контролам, чтобы улучшить их работу для незрячих людей и в целом повысить удобство использования приложения.
VoiceOver имеет стандартный набор «особенностей» UITraitCollection, которые вы можете применять к контролам. Важно знать о них заранее, чтобы не придумывать своих решений. Я поделил их на три типа:
Сразу буду показывать на примере экрана с карточкой пиццы:

VoiceOver знает про несколько базовых типов элементов. Часть из них уже настроена в вашем проекте, но всё равно расскажу про них.
Типы контролов используются для навигации: по ним можно быстро перемещаться с помощью ротора [5].
.staticText— для надписей, которые не меняются. Текст просто прочитается. .header — заголовок: Добавить в пиццу, заголовок..button — кнопка. Основной способ подписывать активные контролы: Изменить состав, кнопка..image — картинка. .link — ссылка. Редкий гость в приложениях, частый на сайтах. .searchField — поиск.
Смотрим на примере:

.staticText ставится автоматически для всех надписей, а вот .header для заголовка нужно поставить вручную. При этом нужен и .header и .staticText. .image и подписываем .accessibilityLabel = "Пицца Пепперони Фреш с перцем". Конечно, кнопки закрытия и корзины надо подписать, об этом было в прошлой статье. [1]
У контрола может быть три состояния: обычный, выбранный и отключенный. Интересно, что произносятся они в разное время и могут быть выбраны одновременно:

.selected — добавляет «выбрано» перед названием контрола. Подходит для всех свитчеров и чекбоксов. .notEnabled — добавляет «недоступно». Эта настройка не видна в Interface Builder и управляется только программно.
Пример с добавлением топпингов в пиццу:

С помощью состояний можно объяснить пользователю, что топпинг добавлен. Удобно поправить прямо внутри ячейки. accessibilityTraits это OptionSet, поэтому к нему можно применять методы вставки .formUnion и удаления .formIntersection:
class ToppingCell: UICollectionViewCell {
override var isSelected: Bool {
didSet {
if isSelected {
accessibilityTraits.formUnion(.selected)
} else {
accessibilityTraits.formIntersection(.selected)
}
}
}
...
}
Есть ещё несколько необычных свойств. В нашем случае к экрану с пиццей они не подходят, но я всё равно расскажу про них, потому что информации о них мало. Возможно, это сэкономит вам время.
.summaryElement — первое, что скажет приложение после запуска. Например, приложение погоды после запуска может сразу рассказать о температуре, а музыкальный плеер расскажет о включенной песне и исполнителе. В нашем случае можно говорить статус доставки, если заказа уже оформлен. .updatesFrequently — штука для таймеров. Новое значение будет проговариваться раз в несколько секунд. .causesPageTurn — скролит после прочтения. Вызовется accessibilityScroll(.next) у того контрола, который сможет это обработать. Смотрит по .firstResponder. .startsMediaSession — обычно VoiceOver повторяет название нажатой кнопки, чтобы подтвердить действие. Это мешает, если контрол проигрывает звук. Включите этот трейт, чтобы VoiceOver не повторял название контрола. .playsSound — стоит включить, если вы проигрываете собственный звук при фокусировании на контроле (если я всё правильно понял, сам ни разу не использовал)..allowsDirectInteraction — для рисования и обработки жестов. Контрол сразу обрабатывает касание, будто VoiceOver выключен. .keyboardKey — контрол начинает реагировать, как кнопка на клавиатуре. У VoiceOver есть несколько режимов ввода текста для таких случаев:
— standart typing — как простая кнопка в VoiceOver: сначала наведите фокус на букву, а затем нажмите дважды в любом месте, чтобы написать её. Набирать можно быстрее двумя руками: одним пальцем водить по клавиатуре (буквы будут озвучиваться) и касаться другим пальцем, чтобы подтвердить выбор клавиши.
— touch typing — однорукий ускоренный набор: водите пальцем по клавиатуре, чтобы озвучить кнопки. Отпустите палец, чтобы написать букву.
— direct touch typing — как обычный набор, будто VoiceOver выключен.
Видео про разные способы ввода:
Достаточно поставить галочку в IB, чтобы добавить поведение. С трейтом .adjustable так просто не получится, о нём отдельно.
И последний, особенно важный трейт .adjustable — элемент, который можно регулировать: так работают UIStepper и UISlider. Свайпните такой контрол вверх или вниз, чтобы изменить значение (не забывайте, что свайп влево/вправо переключит фокус на соседний элемент). Если у контрола есть UIPanGestureRecognizer, то можно тапнуть дважды и задержать второй тап, так жест сработает и можно управлять им напрямую, будто VoiceOver выключен.
Примеры применений для .adjustable:
Переключатель теста. Настройка теста состоит из пяти кнопок: три для выбора размера пиццы и две для типа теста. Их стоит сгруппировать и подписать, чтобы вместо пяти осталось две: «Размер, средний. Элемент регулировки» и «Тесто, традиционное. Элемент регулировки».

Нужно сделать в 4 шага:
.adjustable..accessibilityValue.override public func awakeFromNib() {
super.awakeFromNib()
isAccessibilityElement = true // 1
accessibilityTraits = .adjustable // 2
}
extension SegmentedControl {
override public func accessibilityIncrement() { // 3
controller.selectNext(increment: +1)
}
override public func accessibilityDecrement() { // 3
controller.selectNext(increment: -1)
}
public override var accessibilityValue: String? { // 4
get {
return selectedSegment?.accessibilityValue
} set { }
}
}
Теперь после свайпа вверх вызовется accessibilityIncrement(), вы увеличите внутренний счётчик, и VoiceOver прочитает новое значение из accessibilityValue.
Счетчик количества всего на свете. В данном блоке мы видим четыре контрола: кнопка минус, количество, кнопка плюс и цена. Можно объединить их в одну view и превратить в один контрол: «Количество, 1, 575 рублей. Элемент регулировки». После вертикального свайпа изменится количество, а затем произнесётся новое значение вместе с ценой.

Горизонтальные UICollectionView. Оказалось, что .adjustable удобно применять и для горизонтальных UICollectionView. Например, выбрать акцию в меню или машину в такси.

В этот раз мы разобрали трейты: их типы, состояния и поведение. Это стандартный набор для типовых задач. Для сложных контролов можно использовать .adjustable.
В следующий раз посмотрим на решение типовых проблем: порядок обхода, модальные окна, индикаторы загрузки.
Чтобы не пропустить следующую статью, подписывайтесь на мой канал Dodo Pizza Mobile [6].
А ещё у нас сейчас открыта одна вакансия в мобильном направлении. Так что я просто оставлю это здесь: Senior iOS Developer (Нижний Новгород). [7]
Автор: Рубанов Михаил
Источник [8]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ux/347195
Ссылки в тексте:
[1] В первой части: https://habr.com/ru/company/dodopizzadev/blog/481200/
[2] Тип контрола.: #Q0
[3] Состояние контрола.: #Q1
[4] Особые свойства контролов.: #Q2
[5] с помощью ротора: https://support.apple.com/ru-ru/HT204783
[6] на мой канал Dodo Pizza Mobile: https://telegram.im/dodomobile
[7] Senior iOS Developer (Нижний Новгород).: https://apply.workable.com/dodopizza/j/F5003C7FAC/
[8] Источник: https://habr.com/ru/post/488246/?utm_source=habrahabr&utm_medium=rss&utm_campaign=488246
Нажмите здесь для печати.