Focus Order је секвенција у којој елементи интерфејса добијају фокус при навигацији помоћу тастатуре, Switch Control, VoiceOver или TalkBack. У мобилним апликацијама редослед фокуса одређује како корисник путује између контрола помоћу покрета или дугмада. Према W3C WCAG 2.2, Success Criterion 2.4.3, 2023, фокус мора да прати логички редослед који чува смисао садржаја. Кршење овог принципа је један од честих разлога непроласности аудита приступачности.
Главно
Focus Order је секвенција у којој корисник путује између интерактивних елемената помоћу алтернативних метода уноса: тастатуре (Tab), Switch Control (корак по корак), VoiceOver (покрет удесно/улево) или TalkBack. За разлику од миша или екрана осетљивог, где корисник бира елеменат директно, фокусна навигација је линеарна — сваки корак помера фокус на следећи елеменат.
Према Apple HIG, 2024, VoiceOver користи редослед елемената у дрвету приступачности, које се гради на основу визуелног распореда: горњи леви угао → доњи десни угао. Ако екран садржи сложен распоред (колоне, Grid, ZStack), дрво може да не одговара визуелном редоследу.
Принцип WCAG 2.4.3: «Ако се веб страница може узастопно померати кроз секције и редослед фокуса утиче на смисао, онда би фокус требало да прати редослед који чува смисао и могућност управљања». Изузетак: динамички садржај где фокус може да скаче како би привукао пажњу (упозорења, модални прозори).
Корисник Switch Control (особе са моторичким сметњама) креће се кроз елементе аутоматски — циклус за циклусом. Ако је редослед поремећен, корисник троши 3 пута више времена за завршавање обрасца. Према Deque University, 2024, исправан Focus Order скраћује време попуњавања обрасца за 60% за кориснике помоћних технологија.
Посебну пажњу треба посветити модалним прозорима. Након отварања модалног прозора, фокус се мора одмах померити на први интерактивни елеменат унутар модала (обично дугме «Затвори» или «Потврди»). Након затварања — вратити се на елеменат који је позвао модални прозор. Ово је захтев WCAG 2.4.3 и истовремено честа грешка.
У iOS-у VoiceOver аутоматски гради редослед на основу геометрије: елементи се сортирају по Y, затим по X. За екране са сложеном структуром, овај редослед може бити неисправан — програмер мора да интервенише.
Основни алати:
Пример постављања прилагођеног редоследа за картицу производа:
class ProductCardView: UIView {
let titleLabel = UILabel()
let priceLabel = UILabel()
let buyButton = UIButton()
override var accessibilityElements: [Any]? {
get {
return [titleLabel!, priceLabel!, buyButton!]
}
set {}
}
}
За програмско померање фокуса након акције:
UIAccessibility.post(
notification: .layoutChanged,
argument: newlyAddedItem
)
Својство shouldGroupAccessibilityElement је корисно за картице у колекцијама. Ако се постави true на родитељској картици, VoiceOver доживљава целу картицу као један елеменат. Корисник може два пута да додирне да би активирао целу картицу или подеси ротор за навигацију унутар. Препоручује се за UICollectionViewCell и UITableViewCell.
У Android-у TalkBack такође користи геометријски редослед, али приоритет имају експлицитни атрибути nextFocus*. Ови атрибути се постављају у XML-у или програмски:
| Атрибут | Намена | Примјер |
|---|---|---|
| nextFocusDown | Елеменат при навигацији надоле | @+id/field_email |
| nextFocusUp | Елеменат при навигацији нагоре | @+id/field_name |
| nextFocusLeft | Елеменат с леве стране | @+id/btn_back |
| nextFocusRight | Елеменат с десне стране | @+id/btn_next |
Примјер за форму регистрације:
<EditText
android:id="@+id/field_email"
android:nextFocusDown="@+id/field_password" />
<EditText
android:id="@+id/field_password"
android:nextFocusDown="@+id/btn_submit" />
За RecyclerView редослед фокуса је динамички — одређује га адаптер. Ако ћелије имају сложену структуру, поставите descendantFocusability = «beforeDescendants» и одредите редослед у чвору елемента листе. За Jetpack Compose редослед фокуса се поставља путем Modifier.focusOrder() и FocusOrder. Приоритет: previous (подређени), next (следећи), custom key.
Ако је елеменат превише мали за фокус (мањи од 44pt), повећајте hit area путем TouchDelegate у iOS-у или minWidth/minHeight у Android-у. Према Google Material Design, 2024, минимална површина додира је 48×48 dp. VoiceOver и TalkBack се фокусирају на bounding box елемента. Елементи мањи од 30pt могу бити недоступни за гестовни фокус — корисник физички не може да их додирне прстом.
Скакући фокус — када након акције (нпр. брисања елемента) фокус прелази на почетак листе или на системско дугме «Назад». Корисник VoiceOver-а губи контекст. Решење: програмски померити фокус на елеменат најближи обрисаном.
Невидљиви фокус — елеменат добија фокус, али нема визуелног индикатора (корисници тастатуре не виде где су). У iOS-у проверавајте UIAccessibility.isVoiceOverRunning за прилагођене индикаторе. Према Deque University, 2024, невидљиви фокус је други по учесталости разлог неуспеха аудита приступачности.
Модални прозори — фокус остаје на позадинском садржају након отварања модалног прозора. У iOS-у модални поглед аутоматски захвата фокус ако је постављен modalPresentationStyle = .pageSheet. У Android-у користите setFocusable(true) на контејнеру дијалога.
Обрнути проблем: фокус застаје унутар модалног прозора и не може изаћи (осим затварања). Ово је дозвољено само за модалне прозоре — корисник мора свесно да затвори прозор. За обичне екране, замка фокуса је критична грешка. Решење: уверите се да последњи елеменат модалног прозора (дугме «Затвори») преноси фокус назад.
За прилагођене екране (мапе, платна, игрице) аутоматски геометријски редослед није примењив. Програмер мора да изгради дрво приступачности ручно. У iOS-у за ово се препоручује метода UIAccessibilityContainer.
Примјер за прилагођени платно:
class CanvasView: UIView {
var shapes: [ShapeView] = []
override var accessibilityElements: [Any]? {
get {
// Сортирамо облике по Z-индексу, а не по геометрији
return shapes.sorted { $0.zIndex < $1.zIndex }
}
set {}
}
}
У Android-у за прилагођени View препоручите onInitializeAccessibilityNodeInfo:
override fun onInitializeAccessibilityNodeInfo(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(info)
info.addChild(firstElement)
info.addChild(secondElement)
info.isFocusable = true
}
За динамичке листе (чат, фед вести) након додавања елемента позивите померање фокуса на први нови елеменат. У iOS-у: UIAccessibility.post(notification: .layoutChanged, argument: newMessage). У Android-у: sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED).
iOS аутоматски одређује област фокуса на основу frame-а елемента. Ако елеменат има трансформацију (transform, rotation), VoiceOver се може фокусирати на погрешној области. Експлицитно поставите accessibilityFrame у координатама екрана: element.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(element.bounds, in: element). Ово гарантује да ће VoiceOver освијетлити праву област.
За анимиране екране (UIKit Dynamics, Lottie, SpriteKit) програмски фокус је посебно важан. VoiceOver не може да изгради дрво приступачности за динамички покретне елементе. Поставите isAccessibilityElement = false на контејнерима анимације и true само на интерактивним елементима унутар.
Ручно тестирање: укључите VoiceOver (iOS) или TalkBack (Android), повуците покретом удесно кроз целу секвенцију. Фокус мора да прати визуелни редослед — с лева надесно, одозгор надоле. Сваки интерактивни елеменат мора да добије фокус тачно једанпут.
Аутоматизовано тестирање је отежано, али могуће:
func testKeyboardFocusOrder() {
let app = XCUIApplication()
app.launch()
app.textFields["Email"].tap()
// Tab — само са хардверском тастатуром
}
За Android користите Accessibility Testing Framework:
@Test
fun testFocusOrder() {
onView(withId(R.id.fieldEmail))
.check(matches(isFocusable()))
onView(withId(R.id.fieldEmail))
.perform(focus())
onView(withId(R.id.fieldPassword))
.check(matches(isFocused()))
}
Најпозданија метода је UI тест сценарија: попуните образац корак по корак (Email → Лозинка → Пошаљи), проверавајући да сваки корак успешно завршава. Ако је редослед фокуса поремећен, сценарије ће пасти при покушају интеракције са елементом изван фокуса.
Алат Accessibility Inspector у Xcode-у приказује пуно дрво приступачности. Можете проћи кроз елементе у VoiceOver редоследу и видети тачан пут фокуса. Користите картицу «Audit» за аутоматско претрагивање повреда Focus Order-а.
Често постављана питања
WCAG 2.4.3 (Focus Order) је критеријум успеха нивоа A. Захтева да редослед фокуса чува смисао садржаја при секвенцијалној навигацији. Кршење се сматра критичним и блокира сертификацију.
Сакривени елементи треба да имају isAccessibilityElement = false у iOS-у или visibility = gone/invisible у Android-у. При појављивању — програмски померите фокус путем UIAccessibility.post(notification: .layoutChanged).
iOS управља путем accessibilityElements и shouldGroupAccessibilityElement, Android — путем атрибута nextFocus* и AccessibilityNodeInfo. Принцип је исти: подразумевани геометријски редослед са могућношћу препоручивања.
Поставите descendantFocusability = «beforeDescendants» на коренском елементу и подесите редослед у адаптеру путем onInitializeAccessibilityNodeInfo за сваку ћелију.
Повежите хардверску тастатуру путем Bluetooth-а или USB-а. У iOS-у притисните Tab за померање фокуса. У Android-у укључите TalkBack и користите тастере Tab и стрелице.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође