Accessibility Trait: istota, jakie są rodzaje i jak działają w programowaniu

Autor: IT Sectr Opublikowano: 2026-05-16 Czas czytania: 9 min

Accessibility Trait — to właściwość elementu iOS, która określa jego rolę i zachowanie dla VoiceOver. Trait informuje czytnik ekranu, jak element powinien być odczytany i jakie gesty są dostępne: czy jest przyciskiem, nagłówkiem, linkiem czy polem wyszukiwania. Według danych Apple UIAccessibilityTraits, 2024, system obsługuje 15+ stałych, które można łączyć za pomocą maski bitowej. Prawidłowo wybrany trait oszczędza do 50% czasu nawigacji użytkownikom VoiceOver.

Najważniejsze

  • Accessibility Trait — rola elementu iOS dla VoiceOver; ustawiana przez stałe UIAccessibilityTraits
  • Traity można łączyć za pomocą operatora | do tworzenia złożonych ról (przycisk + wybrany)
  • Każdy element może mieć wiele traitów jednocześnie, ale nie więcej niż 3-4, aby uniknąć zamieszania
  • Nieprawidłowy trait (np. StaticText dla przycisku) psuje scenariusz interakcji: użytkownik nie wie, czy gest jest dostępny
  • W Androidzie odpowiednikiem są atrybuty role i className w AccessibilityNodeInfo

Czym jest Accessibility Trait

Accessibility Trait — flaga ustawiana na elemencie UIView w celu wskazania jego semantycznej roli dla VoiceOver. Trait jest jednym z trzech komponentów triady accessibility Apple: Label (nazwa), Hint (opis), Trait (rola). iOS używa maski bitowej UIAccessibilityTraits (UInt64), gdzie każdy bit odpowiada określonej roli. VoiceOver odczytuje rolę po Label i Hint: „Przycisk Wyślij. Otworzy formularz” — „Przycisk” został dodany dzięki traitowi UIAccessibilityTraitButton.

Domyślnie UIButton otrzymuje UIAccessibilityTraitButton, UILabel — UIAccessibilityTraitStaticText, UIImageView — UIAccessibilityTraitImage. W przypadku niestandardowych kontrolek programista ma obowiązek ręcznego ustawienia traita. Apple Human Interface Guidelines, 2024, nazywają to „jednym z najważniejszych kroków w zapewnieniu accessibility”.

Bez prawidłowego traita użytkownik nie wie, jaki gest zastosować: pojedyncze dotknięcie (aktywacja przycisku), podwójne dotknięcie (powiększenie) lub gest przesunięcia (przełącznik). Trait decyduje, które gesty VoiceOver aktywuje na elemencie.

Implementacja techniczna UIAccessibilityTraits

UIAccessibilityTraits — to typealias UInt64. Każdy trait to stała, w której ustawiony jest dokładnie jeden bit. Na przykład UIAccessibilityTraitButton = 0x0000000000000001, UIAccessibilityTraitLink = 0x0000000000000002, UIAccessibilityTraitHeader = 0x0000000000000008. Kombinację osiąga się przez bitowe OR: 0x0001 | 0x0008 = 0x0009. VoiceOver analizuje maskę i określa zachowanie.

Główne typy traitów iOS

iOS udostępnia ponad 15 stałych traitów. Omówmy główne, używane w 90% scenariuszy:

TraitStałaZachowanie VoiceOver
ButtonUIAccessibilityTraitButtonAktywacja przez podwójne dotknięcie
HeaderUIAccessibilityTraitHeaderSzybka nawigacja po nagłówkach
LinkUIAccessibilityTraitLinkAktywacja jako link
StaticTextUIAccessibilityTraitStaticTextTylko do odczytu, bez aktywacji
SearchFieldUIAccessibilityTraitSearchFieldPole wyszukiwania ze specjalnym zachowaniem
ImageUIAccessibilityTraitImageObraz, bez gestu aktywacji
SelectedUIAccessibilityTraitSelectedStan „wybrany”
PlaysSoundUIAccessibilityTraitPlaysSoundOdtwarza dźwięk przy aktywacji
KeyboardKeyUIAccessibilityTraitKeyboardKeyKlawisz klawiatury
TabBarUIAccessibilityTraitTabBarElement paska kart

Stałe są dostępne w UIKit od iOS 3.0. W iOS 14+ dodano obsługę UIAccessibilityTraits w SwiftUI poprzez modyfikator .accessibilityAddTraits().

Rzadkie, ale przydatne traity

UIAccessibilityTraitAdjustable — dla regulowanych wartości (suwaki, pokrętła, suwaki głośności). VoiceOver pozwala przesuwać w górę/w dół w celu zmiany wartości z krokiem określonym przez accessibilityIncrement i accessibilityDecrement. UIAccessibilityTraitUpdatesFrequently — dla elementów o często zmieniającej się wartości (timer, wskaźnik ładowania). VoiceOver nie odczytuje wartości przy każdej zmianie, ale robi pauzę. UIAccessibilityTraitAllowsDirectInteraction — dla elementów, z którymi użytkownik może wchodzić w interakcję bezpośrednio (klawiatura, rysowanie), z pominięciem gestów VoiceOver.

Łączenie traitów

Jeden element może mieć kilka traitów jednocześnie — kombinację określa się przez bitowe OR (|). Przykład: przycisk, który jest aktualnie wybrany — Button | Selected. VoiceOver wypowie: „Wybrano. Filtrowane według ceny. Przycisk”.

Ustawianie traitów w kodzie:

swift
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)

// Albo przez maskę:
filterButton.accessibilityTraits = [.button, .selected]

Dla niestandardowych UIView, gdzie trait nie jest ustawiony domyślnie:

swift
class CustomToggle: UIControl {
    override var accessibilityTraits: UIAccessibilityTraits {
        get {
            if isOn {
                return [.button, .selected]
            } else {
                return .button
            }
        }
        set {}
    }
}

Zasada łączenia: nie więcej niż 3-4 traity na element. Nadmierne traity (np. Button + Link + Header) sprawiają, że deklaracja VoiceOver jest zbyt długa i myląca. Według Apple, „każda dodatkowa właściwość zwiększa obciążenie poznawcze użytkownika”.

SwiftUI: modyfikatory traitów

W SwiftUI traity ustawia się za pomocą modyfikatorów .accessibilityAddTraits() i .accessibilityRemoveTraits(). Przykład: Text("Nagłówek").font(.largeTitle).accessibilityAddTraits(.isHeader). Modyfikator .isHeader dodaje UIAccessibilityTraitHeader. Lista traitów SwiftUI: .isButton, .isHeader, .isLink, .isSelected, .isImage, .isSearchField, .isKeyboardKey, .isStaticText, .isSummaryElement, .isToggle, .playsSound, .startsMediaSession, .updatesFrequently, .allowsDirectInteraction, .causesPageTurn, .isModal, .tabBar.

Typowe błędy przy wyborze traita

StaticText zamiast Button — niestandardowa kontrolka, wizualnie wyglądająca jak przycisk, otrzymuje domyślnie trait StaticText. VoiceOver nie oferuje gestu aktywacji, użytkownik nie może „nacisnąć” elementu. Rozwiązanie: jawnie ustaw .button.

Image bez traita — UIImageView z włączoną accessibility otrzymuje trait Image, nawet jeśli w rzeczywistości jest to przycisk do powiększania zdjęcia. Przypisz .button i Label „Powiększ zdjęcie”. Według WWDC 2023, „Deliver an Exceptional Accessibility Experience”, 40% regresji accessibility w nowych wersjach aplikacji wynika właśnie z niezgodności traita.

Header na każdym elemencie — trait Header jest przeznaczony dla strukturalnych nagłówków ekranu. Jeśli uczynisz każdy UILabel nagłówkiem, rotor VoiceOver w trybie „Nagłówki” stanie się bezużyteczny — będzie zatrzymywał się na każdym słowie.

Jak naprawić: lista kontrolna

  • Każdy interaktywny niestandardowy element otrzymuje trait Button, Link lub Adjustable
  • Nagłówki sekcji otrzymują trait Header (nie StaticText)
  • Obrazy-przyciski otrzymują trait Button + Selected w stanie selected
  • Elementy bez gestu — StaticText lub Image (tylko do odczytu)

Błędy regresyjne przy zmianie UIButton na UIControl

Najczęstsza przyczyna utraty traita — refaktoring: programista zastępuje UIButton na UIControl dla niestandardowego wyglądu. UIButton automatycznie otrzymuje trait Button, UIControl — nie. Po refaktoringu należy jawnie ustawić accessibilityTraits = .button. Dodaj sprawdzenie do code review: „Jeśli zastąpiono UIButton na UIControl — sprawdź trait”.

Traity i stany dynamiczne

Dla elementów o zmieniającym się stanie (np. przycisk polubienia) trait powinien zmieniać się dynamicznie. W stanie „nie polubione” — Button, w stanie „polubione” — Button + Selected + Image (jeśli ikona). VoiceOver zmienia komunikat: „Lubię to. Przycisk” vs „Wybrano. Lubię to. Przycisk”. Użyj accessibilityValue do przekazania stanu, jeśli trait Selected jest niewystarczający. Dotyczy przycisków subskrypcji, ulubionych, filtrów i przełączników.

Odpowiednik w Androidzie: role i className

W Androidzie nie ma bezpośredniego odpowiednika traitów. Zamiast maski bitowej używane są:

  • className — wartość AccessibilityNodeInfo.className (android.widget.Button, android.widget.TextView)
  • role — atrybut w XML (rola określana przez typ View)
  • stateDescription — odpowiednik Selected: dodanie opisu stanu (włączone/wyłączone)

Dla niestandardowych View w Androidzie trzeba nadpisać onInitializeAccessibilityNodeInfo:

kotlin
class CustomButton @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    override fun onInitializeAccessibilityNodeInfo(
        info: AccessibilityNodeInfo
    ) {
        super.onInitializeAccessibilityNodeInfo(info)
        info.className = "android.widget.Button"
        info.isClickable = true
    }
}

Programiści Flutter powinni używać parametru semanticsRole w widgecie Semantics: button, header, image, link, textField i inne. Dodatkowo dostępne są semanticsLabel i semanticsHint — pełny odpowiednik triady iOS Label + Hint + Trait.

Odpowiedniki webowe: rola WAI-ARIA

Dla wersji webowych aplikacji mobilnych (PWA, WebView) używany jest atrybut role z WAI-ARIA: role="button", role="heading", role="link". To bezpośredni odpowiednik accessibilityTraits. W aplikacjach hybrydowych sprawdź, czy WebView przekazuje role ARIA do natywnej warstwy accessibility. W tym celu użyj protokołu UIAccessibilityContainerDataTable w iOS lub setAccessibilityDelegate w Androidzie. WebView z włączonym JavaScript może nie przekazywać ról ARIA poprawnie — testuj osobno.

AccessibilityNodeInfo: dodatkowe akcje

W Androidzie można dodać niestandardowe akcje do AccessibilityNodeInfo: AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK i ACTION_LONG_CLICK. To odpowiednik traita Button z dodatkowymi gestami. Dla suwaków użyj ACTION_SET_PROGRESS — odpowiednik Adjustable. Dla Spinner i DatePicker — ACTION_SET_SELECTION, ACTION_SET_DATE i ACTION_SET_TIME.

Sprawdzanie i testowanie traitów

Xcode Accessibility Inspector — główne narzędzie dla iOS: wybierz element i zobacz pole Traits. Pokaże ono listę ustawionych traitów. Rotor VoiceOver w trybie „Elementy” pozwala przejść przez wszystkie kontrolki ekranu.

Zautomatyzowany test w Swift do sprawdzania traita:

swift
func testSubmitButtonTrait() {
    let app = XCUIApplication()
    app.launch()
    let submitButton = app.buttons["Wyślij"]
    XCTAssertTrue(submitButton.isEnabled)
    // XCUIElement nie zapewnia bezpośredniego dostępu do traitów
    // Sprawdzenie przez aktywację gestu
    submitButton.tap()
    XCTAssertTrue(app.staticTexts["Formularz wysłany"].exists)
}

Ręczne sprawdzanie przez VoiceOver: włącz VoiceOver, przesuń palcem do elementu, dotknij dwukrotnie — element powinien się aktywować, jeśli to Button. Jeśli element nie reaguje na podwójne dotknięcie, trait jest nieprawidłowy. Użyj gestu Rotor do przełączania między trybami („Nagłówki”, „Linki”, „Przyciski”) — każdy tryb pokaże tylko elementy z odpowiednim traitem.

Testy jednostkowe traitów w iOS

Przed iOS 14 testy jednostkowe nie miały bezpośredniego dostępu do accessibilityTraits. Od iOS 14 właściwość jest dostępna: XCTAssertEqual(customButton.accessibilityTraits, .button). Użyj tego w testach modułowych do sprawdzania niestandardowych kontrolek. Zaleca się testowanie każdej nowej niestandardowej UIView pod kątem poprawności traita, zwłaszcza po refaktoringu lub zmianie klasy nadrzędnej.

Często zadawane pytania

Ile traitów można ustawić dla jednego elementu?

Do 3-4 traitów na element. Większa liczba sprawia, że deklaracja VoiceOver jest zbyt rozwlekła. Używaj kombinacji: Button + Selected, Header + StaticText.

Jaki trait ma domyślnie UIButton?

UIAccessibilityTraitButton. iOS automatycznie ustawia go dla wszystkich instancji UIButton. Jeśli dziedziczysz po UIView i imitujesz przycisk, trait trzeba ustawić ręcznie.

Czy istnieje trait „Adjustable” i do czego służy?

Tak, UIAccessibilityTraitAdjustable — dla elementów z regulowaną wartością (suwaki, pokrętła, liczniki). VoiceOver pozwala przesuwać w górę/w dół w celu zmiany wartości i odczytuje bieżący stan.

Jak sprawdzić traity w SwiftUI?

Użyj modyfikatora .accessibilityAddTraits(): Text("Nagłówek").font(.title).accessibilityAddTraits(.isHeader). Metoda działa na iOS 14+.

Co się stanie, jeśli nie ustawię traita dla niestandardowej kontrolki?

VoiceOver przypisze trait None. Element nie otrzyma roli — czytnik ekranu odczyta tylko Label bez wskazania typu. Użytkownik nie dowie się, czy gest aktywacji jest dostępny.

Podsumowanie

  • Accessibility Trait — maska bitowa UIAccessibilityTraits, określająca rolę elementu iOS dla VoiceOver (Button, Header, Link, StaticText i inne)
  • Traity łączy się przez bitowe OR ([] w Swift), nie więcej niż 3-4 na element
  • Niestandardowe UIView muszą otrzymać jawny trait — domyślnie może być None lub Image
  • W Androidzie rolę określa się przez className w AccessibilityNodeInfo, w Flutter — przez semanticsRole
  • Nieprawidłowy trait (StaticText dla przycisku) psuje scenariusz VoiceOver: brak gestu aktywacji
  • Sprawdzaj traity przez Accessibility Inspector w Xcode i rotor VoiceOver
  • W SwiftUI używaj .accessibilityAddTraits() do deklaratywnego ustawiania traitów

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również