A thumb pressing a tactile accessibility switch with a scarlet-red rubber dome on a Scandinavian oak surface, headphones blurred beside — the visual marker for mobile-native accessibility-API comparison.
Image description: A thumb pressing a tactile accessibility switch with a scarlet-red rubber dome on a Scandinavian oak surface, headphones blurred beside — the visual marker for mobile-native accessibility-API comparison.

Engineering primer · Mobiele a11y-API's

Mobiele-native toegankelijkheids-API's in 2026: UIAccessibility, AccessibilityNode en het web

Een vergelijkende primer over iOS UIAccessibility, Android AccessibilityNodeInfo en de cross-platform bridges die ze proberen te overbruggen — wat goed mapped, wat niet, en waar mobiele webtoegankelijkheid stil faalt.

Mobiele-native toegankelijkheids-API’s in 2026:
UIAccessibility, AccessibilityNode en het web

iOS en Android stellen elk een volledig uitgeruste toegankelijkheidsboom beschikbaar aan de platform-schermlezer — en die twee bomen zijn het over niets eens na de basisbeginselen van label en rol. We brachten elke primitieve in kaart die VoiceOver en TalkBack in 2026 daadwerkelijk verwerken, de manier waarop React Native, Flutter en Kotlin Multiplatform proberen ze te overbruggen, en de plek waar mobiele WebView-toegankelijkheid stilletjes van een klif valt.

2
native API’s vergeleken
3
cross-platform bridges
28
primitieven in kaart gebracht
13 min lezen
Bijgewerkt mei 2026

1. iOS UIAccessibility — labels, traits, hints, waarden

Elk zichtbaar element op een iOS-scherm heeft een toegankelijkheidsrepresentatie, of kan die hebben. Apple levert die representatie via het informele protocol UIAccessibility, geïmplementeerd door UIView en elk systeembesturingselement, en via UIAccessibilityElement, een lichtgewicht klasse die men toewijst voor de onderdelen van de interface die weliswaar getekend worden maar zelf geen views zijn — tekens in een op maat getekend diagram, glyfen in een Core Graphics-scène, regio’s in een CALayer. VoiceOver, Schakelaarbediening, Volledige toetsenbordtoegang en Stembesturing verwerken allemaal hetzelfde protocol; het eenmalig leren ervan levert vier hulptechnologieën op.

Het protocol stelt vier primitieven beschikbaar die voor vrijwel elk scherm van belang zijn. Het toegankelijkheidslabel is de korte, mensleesbare naam van het element — “Verzenden”, “Profielfoto van Asha”, “Terug”. De toegankelijkheidstraits zijn een bitmask van rolachtige vlaggen — .button, .header, .image, .selected, .adjustable, .staticText, .updatesFrequently — die VoiceOver vertellen hoe het zich rond het element moet gedragen en welke gebaren ingeschakeld moeten worden. De toegankelijkheidswaarde is een tekenreeksrepresentatie van de huidige toestand (“Aan”, “75%”, “donderdag 22 mei”). De toegankelijkheidshint is de langere, optionele toelichting (“Dubbeltik om de fotokijker te openen”) die VoiceOver na een vertraging uitspreekt als de gebruiker niet reageert op het label alleen.

De vier primitieven werken samen. Een schakelaar klinkt als label + trait + waarde: “Wi-Fi, schakelaarknop, Aan”. Een schuifregelaar klinkt als label + trait + waarde + hint: “Volume, instelbaar, 60 procent, veeg omhoog of omlaag om aan te passen”. Een op maat getekende diagrambalk klinkt als een reeks UIAccessibilityElements, elk met een label, een waarde en een frame in zijn container. De reeks is het API-oppervlak — VoiceOver doorloopt het lineair wanneer de gebruiker naar rechts veegt, en respecteert de volgorde waarin de elementen via de accessibilityElements-array van de container worden gepubliceerd.

SwiftUI is hetzelfde protocol, met een vriendelijker facade

De view-modifiers .accessibilityLabel(), .accessibilityValue(), .accessibilityHint() en .accessibilityAddTraits() in SwiftUI compileren naar dezelfde UIAccessibility-eigenschappen op de onderliggende UIView. SwiftUI voegt ook .accessibilityElement(children:) toe, waarmee het “tekens in een diagram”-probleem declaratiever wordt opgelost dan de UIKit-aanpak — maar het runtime-contract dat VoiceOver ziet, is identiek. De UIKit-namen leren is nog steeds de moeite waard, omdat elk Apple-voorbeeld, elk Stack Overflow-antwoord en elke toegankelijkheidsaudit er gebruik van maakt.


2. Android AccessibilityNodeInfo — rollen, acties, importantForAccessibility

Android volgt een andere route. Waar iOS toegankelijkheid koppelt aan een plat protocol op elke view, serialiseert Android de volledige toegankelijkheidsboom als een graaf van AccessibilityNodeInfo-objecten, elk een momentopname van een view op het moment dat een TalkBack-query binnenkomt. Het framework stelt de momentopnamen lui samen; een View publiceert zijn knooppunt door onInitializeAccessibilityNodeInfo() te overschrijven (of in Compose door semanticsmodifiers in te stellen), en het platform weeft de ouder-kindrelaties samen tot een boom die de viewhiërarchie weerspiegelt.

De primitieven verschillen op drie betekenisvolle manieren van iOS. Ten eerste stelt Android een rol beschikbaar via een tekenreeks-getypt className-veld — android.widget.Button, android.widget.CheckBox, android.widget.EditText. TalkBack leest de klassenaam en bepaalt hoe het de aankondiging doet (“knop”, “selectievakje”, “invoervak”). Compose vertaalt zijn Role.Button-, Role.Checkbox- en Role.RadioButton-semantiek naar hetzelfde veld. De rol is gedetailleerder dan een iOS trait-bitmask, maar ook rigider — er is geen “volledig aangepaste” rol, tenzij men de aankondiging als “view” accepteert.

Ten tweede stelt Android interactiviteit voor als een set acties gekoppeld aan het knooppunt: ACTION_CLICK, ACTION_LONG_CLICK, ACTION_SCROLL_FORWARD, ACTION_SET_TEXT, ACTION_FOCUS en een lange lijst aangepaste acties die men kan registreren met AccessibilityNodeInfo.AccessibilityAction. TalkBack maakt de aangepaste acties zichtbaar via de “acties”-rotor — de gebruiker veegt omhoog met één vinger en hoort elke aangepaste actie bij naam. iOS heeft hetzelfde concept (UIAccessibilityCustomAction), maar op Android is de actielijst het oppervlak; op iOS is het gebarenvocabulaire dat.

Ten derde heeft Android importantForAccessibility, een per-view-enum (auto, yes, no, noHideDescendants) die bepaalt of het knooppunt überhaupt in de boom verschijnt. noHideDescendants is het krachtigste instrument in Android-toegankelijkheid en het meest vergeten — het verwijdert de volledige deelboom uit TalkBacks doorloop, het equivalent van aria-hidden=“true” op het web. iOS heeft geen exacte tegenhanger; het dichtstbijzijnde is het instellen van accessibilityElements op een lege array op de container, waardoor alleen de directe kinderen van de container worden verwijderd, niet de hele deelboom.

De “live region”-mismatch

Android stelt ViewCompat.setAccessibilityLiveRegion() beschikbaar met drie waarden: none, polite, assertive. Het vocabulaire lijkt op ARIA — bijna. TalkBack respecteert de beleefdheidsniveaus betrouwbaar. iOS heeft niets vergelijkbaars op protocolniveau: updates worden aangekondigd door UIAccessibility.post(notification: .announcement, argument: “Opgeslagen”) aan te roepen, een imperatieve eenmalige aankondiging die niet aan een view is gekoppeld. Cross-platform bridges moeten het ene bovenop het andere nabootsen, en de impedantiemismatch is zichtbaar in elk framework dat in sectie 3 wordt besproken.


3. Cross-platform bridges — React Native, Flutter, Kotlin Multiplatform

Elk cross-platform mobiel framework moet de twee bovenstaande API’s nemen en één frameworkvormig oppervlak presenteren. Geen enkel slaagt daar volledig in. De drie benaderingen domineren de markt in 2026 — React Native, Flutter en Kotlin Multiplatform met Compose Multiplatform — elk een iets andere afruil tussen lekkage en abstractie.

React Native 0.76
JS-bridge naar native UIKit en Android View
De meest expliciete mapping — en de meest lekkende
iOS-bridgeaccessibilityLabel, accessibilityHint, accessibilityRole, accessibilityState op Pressable en View mappen vrijwel 1:1 naar UIAccessibility — maar de rolnamen zijn het React Native-vocabulaire, niet het iOS-vocabulaire.
Android-bridgeDezelfde JS-props mappen naar AccessibilityNodeInfo via een Yoga-side-adapter; accessibilityRole=“button” stelt className in op android.widget.Button.
ValkuilDe prop accessibilityLiveRegion is alleen voor Android — op iOS doet hij stilletjes niets, en men moet AccessibilityInfo.announceForAccessibility() handmatig aanroepen.
Flutter 3.27
Aangepaste rendering · synthetische a11y-boom
De meest uniforme — en de meest ondoorzichtige
AanpakFlutter rendert alles op een Skia-canvas, waardoor het zijn eigen SemanticsNode-boom opbouwt en die op aanvraag naar het platform serialiseert.
iOS-padSemanticsNodes worden vertaald naar UIAccessibilityElement-instanties op de Flutter-view, waarbij traits worden gemapt vanuit de SemanticsAction- en SemanticsFlag-sets.
Android-padDezelfde SemanticsNode-boom wordt geserialiseerd naar AccessibilityNodeInfo-knooppunten door Flutters Android-view; acties worden AccessibilityActions; live region wordt SemanticsFlag.isLiveRegion.
Kotlin Multiplatform · Compose Multiplatform
Gedeelde Compose-runtime · per-target a11y
Het nieuwste, met de meest platform-vormige naden
AanpakCompose’s Modifier.semantics { } definieert rollen en acties eenmalig; elk target vertaalt hetzelfde semanticsblok naar zijn eigen native a11y-API.
iOS-targetDe Compose-for-iOS-runtime doorloopt de semanticsboom en construeert UIAccessibilityElements — maar de iOS-implementatie is jonger dan de Android-implementatie en mist nog diverse semantische soorten.
Android-targetHet volwassen pad: semantiek wordt AccessibilityNodeInfo via dezelfde compose-ui-semantics-laag die Android-native Compose gebruikt.

Het patroon bij alle drie is hetzelfde: een synthetische, frameworkvormige semantische boom aan de ene kant, twee platform-vormige toegankelijkheidsbomen aan de andere kant, en een vertaler tussenin die de eenvoudige gevallen goed afhandelt en de complexe gevallen met merkbaar kwaliteitsverlies. De eenvoudige gevallen — een knop met een label, een afbeelding met alternatieve tekst, een koptekst — gaan verliesvrij door elk framework en worden correct aangekondigd op beide platforms. De complexe gevallen — een aangepast gebaar met twee-vinger-veegbewegingen, een diagram waarvan de elementen een focusseerbare groep moeten vormen, een live region die op iOS moet worden geactiveerd zonder een view-gebonden beleefdheidssetting — laten het vocabulaire van het onderliggende platform lekken in de cross-platform code, of slagen er simpelweg niet in te vertalen.

”De eerste 80 procent van mobiele toegankelijkheid is identiek in elk framework. De laatste 20 procent is waar elk framework onthult in welke native API het heimelijk denkt.”

— Engineering-redactie van Disability World, mei 2026

4. De WebView-kloof — wanneer mobiele webtoegankelijkheid stilletjes faalt

Zowel iOS als Android renderen webinhoud via een systeem-WebView — WKWebView op iOS, android.webkit.WebView (of Chrome Custom Tabs) op Android. In beide gevallen is de WebView een black box vanuit het perspectief van de host-app: de app ziet één view, maar de schermlezer ziet de volledige DOM-toegankelijkheidsboom erin. De bridge tussen de twee bomen is de plek waar verrassend veel mobiele toegankelijkheid stilletjes misgaat.

Het mechanisme is, op het oog, eenvoudig. Wanneer de focus van een schermlezer een WebView binnenkomt, leest het platform de toegankelijkheidsboom van het document rechtstreeks uit de browserengine — WebKit op iOS, Blink op Android — en doorloopt het als een deelboom van de boom van de host-app. De rollen, labels en ARIA-attributen van het web worden in real time vertaald naar het vocabulaire van het platform. Een button-element zonder expliciete rol in de WebView wordt op beide platforms als een knop voorgelezen; een aria-live=“polite”-regio wordt op beide correct aangekondigd; een aria-label op een koppeling verschijnt als het toegankelijkheidslabel van de koppeling. In de eerste drie jaar van mobiel-webgebruik werkte dit gewoon.

De klif verschijnt op drie plaatsen. Ten eerste zijn aangepaste gebaren die in de host-app zijn gedefinieerd — een twee-vinger-veeg om te sluiten, een magic-tap om te spelen en te pauzeren — onzichtbaar voor de inhoud van de WebView; ze worden geactiveerd op het verkeerde doel of helemaal niet wanneer de focus binnen het document is. Ten tweede concurreren de UIAccessibilityElements van de host-app die boven de WebView worden getekend (een zwevende actieknop, een aangepaste werkbalk) met de boom van de WebView om de doorloopvolgorde, en de resulterende leesvolgorde is niet-deterministisch over iOS-versies heen. Ten derde — en dit is de grootste enkelvoudige faalwijze in mobiele webtoegankelijkheid — respecteert de WebView op iOS de beleefdheidsniveaus van aria-live niet op de manier waarop Safari dat doet in een tabblad: de aankondigingsroodleiding van WKWebView laat het onderscheid tussen polite en assertive vallen, zodat elke live-update als polite wordt behandeld, ongeacht de opmaak.

Twee weergaven van dezelfde DOM
In een tabblad van Mobile Safari
<div role="alert" aria-live="assertive">
  Verbinding verbroken — opnieuw proberen.
</div>

VoiceOver in een normaal Safari-tabblad onderbreekt de huidige uitspraak en spreekt het bericht onmiddellijk uit. Het assertive-beleefdheidsniveau wordt end-to-end door WebKit gehonoreerd.

Dezelfde DOM in een WKWebView
<div role="alert" aria-live="assertive">
  Verbinding verbroken — opnieuw proberen.
</div>

Dezelfde opmaak, dezelfde browserengine — maar de toegankelijkheidsbrug van WKWebView naar UIKit degradeert de aankondiging naar een uitgesteld polite-bericht. De gebruiker hoort het na een vertraging, soms nadat hij al in het nu kapotte formulier heeft getypt.

De cross-platform oplossing die daadwerkelijk werkt

Voor aankondigingen binnen een WebView is het enige betrouwbare cross-platform patroon in 2026 om een JavaScript-bridge naar de host-app beschikbaar te stellen — een kleine postMessage-handler — en assertieve aankondigingen uit de DOM te routeren, via de host-app, en terug via UIAccessibility.post(notification: .announcement, …) op iOS of announceForAccessibility() op Android. Het aria-live van het web overleeft alleen voor werkelijk beleefde berichten waarbij een vertraging van enkele seconden acceptabel is.


5. De mappingtabel — wat overeenkomt met wat

Er zijn 28 primitieven in kaart gebracht die VoiceOver en TalkBack in de praktijk daadwerkelijk verwerken — de unie van het iOS UIAccessibility-protocoloppervlak, het Android AccessibilityNodeInfo-oppervlak en de meest gebruikte React Native- en Flutter-cross-platform-props. De onderstaande tabel bevat alleen de betwiste rijen: de primitieven waarbij de mapping onvolledig, asymmetrisch of verrassend is. Rijen waarbij de mapping correct is (label, knoprol, afbeeldingsrol, koptekst) zijn omwille van de lengte weggelaten.

MogelijkheidiOS UIAccessibilityAndroid AccessibilityNodeInfoReact Native 0.76Flutter 3.27
Hinttekst (langere toelichting)accessibilityHinttooltipText (API 28+)accessibilityHint (alleen iOS)SemanticsProperties.hint
Live region-beleefdheidN/A — alleen imperatieve postsetAccessibilityLiveRegion()accessibilityLiveRegion (alleen Android)SemanticsFlag.isLiveRegion
Deelboom verbergen voor a11yaccessibilityElementsHidden (alleen kinderen)importantForAccessibility=“noHideDescendants”accessibilityElementsHidden / importantForAccessibilityExcludeSemantics-widget
Aangepaste actie (rotor / menu)UIAccessibilityCustomActionAccessibilityNodeInfo.AccessibilityActionaccessibilityActions + onAccessibilityActionSemanticsAction met aangepast label
Instelbare / schuifregelaar-semantiekUIAccessibilityTraitAdjustable + accessibilityIncrementRangeInfo + ACTION_SCROLL_FORWARDaccessibilityRole=“adjustable” + handlersSlider stelt SemanticsAction.increase beschikbaar
KoptekstniveauUIAccessibilityTraitHeader (geen niveau)setHeading(true) (geen niveau)accessibilityRole=“header” (geen niveau)SemanticsProperties.headingLevel (1–6)
Geselecteerde / omgeschakelde toestandUIAccessibilityTraitSelectedsetSelected(true) + setCheckable()accessibilityState={selected, checked}SemanticsFlag.isSelected
Groep / containersemantiekshouldGroupAccessibilityChildrensetScreenReaderFocusable(true)accessible={true} op bovenliggende elementMergeSemantics-widget
Eenmalig bericht aankondigenUIAccessibility.post(.announcement, …)view.announceForAccessibility()AccessibilityInfo.announceForAccessibility()SemanticsService.announce()

Drie patronen springen uit de tabel. Ten eerste is de asymmetrie rond live regions de grootste bron van cross-platform divergentie — Android heeft een per-view beleefdheidssetting, iOS heeft alleen een globale imperatieve post, en elk bovenstaand framework is gedwongen het verschil te verbergen. Ten tweede zijn koptekstniveaus de ene plek waar Flutter daadwerkelijk verbetert op beide native platforms; de iOS- en Android-primitieven weten alleen “dit is een koptekst”, niet “dit is een H3 onder een H2”. Ten derde is de primitieve “verbergen voor toegankelijkheid” flexibeler op Android dan op iOS — noHideDescendants verbergt een hele deelboom in één beweging, terwijl iOS vereist dat men de kinderen van elke container afzonderlijk verbergt.


6. Het mobiele-native stappenplan

1

Leer het native vocabulaire vóór het framework-vocabulaire

Elk cross-platform framework — React Native, Flutter, Compose Multiplatform — heeft zijn eigen naamgeving voor toegankelijkheidsproppen, en elk van die namen is een kleine leugen over wat het onderliggende platform werkelijk doet. Wanneer een schermlezer niet correct aankondigt, schuilt de fout vrijwel altijd in de native API waarnaar het framework heeft vertaald, niet in de framework-prop die is ingesteld. Lees de UIAccessibility-documentatie en de AccessibilityNodeInfo-documentatie minstens eenmaal door; de framework-documentatie is pas daarna begrijpelijk.

2

Test live-aankondigingen specifiek op iOS

De live-region-asymmetrie uit sectie 2 betekent dat code die ervan uitgaat dat aria-live=“assertive” of accessibilityLiveRegion=“assertive” werkt, op iOS stilletjes zal degraderen. Bouw een kleine testharness die zowel een beleefde als een assertieve aankondiging op beide platforms afvuurt, met VoiceOver en TalkBack op echte apparaten, voordat een functie wordt uitgeleverd waarvan de gebruikerservaring afhangt van het horen van een toestandswijziging.

3

Bridge uit WebViews voor alles wat assertief is

De degradatie door WKWebView van assertieve aankondigingen is geen fout die Apple snel zal oplossen — het is hetzelfde in elke iOS-versie vanaf 14. Als men een hybride app levert waarbij de gebruiker een kritieke fout kan tegenkomen binnen een WebView, routeert men de aankondiging via een JS-bridge naar de host en laat men de host de platform-aankondiging afvuren. Web alleen is niet voldoende.

4

Gebruik de “samenvoegen”- of “groeperen”-semantiek van het framework, niet kind-voor-kind

Zowel iOS (shouldGroupAccessibilityChildren), Android (setScreenReaderFocusable) als Flutter (MergeSemantics) bieden een manier om een visuele cluster — een pictogram plus een label plus een waarde — samen te vouwen tot één toegankelijkheidselement. Gebruik het. Het standaard-gedrag “elk blad is een focusseerbaar element” maakt van een navigatieknop met zes elementen zes VoiceOver-veegbewegingen.

5

Auditeren met Accessibility Inspector en TalkBack Developer Settings

Beide platforms leveren een gratis, officiële inspector voor de live toegankelijkheidsboom — Accessibility Inspector op macOS (gekoppeld aan de verbonden iOS-simulator of het apparaat), en de overlay “Toegankelijkheidsfocus tonen” plus “Ontwikkelaarsinstellingen” op Android. Gebruik ze om de eigen app-boom te lezen zoals de schermlezer hem ziet; ga er niet van uit dat de foutopsporingslogboeken van het framework hetzelfde tonen als wat het platform aan TalkBack toont.


Conclusie: het framework is stroomafwaarts van het platform

Het is verleidelijk te geloven — en de framework-documentatie moedigt dit geloof aan — dat een cross-platform toegankelijkheids-API één verenigde abstractie is over twee gelijkwaardige native API’s. De mappingtabel in sectie 5 weerlegt de unificatie. De twee native API’s zijn onafhankelijk ontworpen, door twee verschillende teams, rond twee verschillende mentale modellen van hoe de schermlezer een document moet doorlopen; de verschillen zijn reëel, ze lekken door elk framework, en de lekkage is zichtbaar in de onderdelen van de gebruikerservaring die het meest van belang zijn — live-updates, aangepaste gebaren, verborgen deelbomen, kopteksthiërarchieën.

Het goede nieuws, na die alinea: de basis mapped. Een knop met een label, een afbeelding met alternatieve tekst, een koptekst boven aan een sectie — die gaan verliesvrij door elk framework en worden correct aangekondigd op beide platforms. Als men alleen die primitieven levert, hoeft men niet na te denken over UIAccessibility of AccessibilityNodeInfo; de standaardinstellingen van het framework zijn eerlijk. De problemen beginnen wanneer de gebruikersinterface iets interessants gaat doen, wat ook precies het moment is dat toegankelijkheid het meest van belang is.

Het stappenplan in sectie 6 is de kortste versie van het argument dat de meeste gebruikers met een beperking tot een werkende ervaring brengt: denk eerst in native primitieven, test op echte apparaten op beide platforms, bridge uit WebViews wanneer dat bedoeld is, groepeer bladknooppunten bewust, en gebruik de officiële inspectors. Het gekozen framework helpt met de eerste 80 procent en maakt ruimte voor de laatste 20 procent. Die laatste 20 procent is waar de gebruiker van een schermlezer leeft.

”VoiceOver en TalkBack lezen twee verschillende documenten uit dezelfde broncode. Of de gebruiker het verschil merkt, is een maatstaf voor hoe goed het platform onder het gekozen framework is begrepen.”

— Engineering-redactie van Disability World, mei 2026