Mobile-native Barrierefreiheits-APIs 2026:
UIAccessibility, AccessibilityNode und das Web
iOS und Android stellen dem plattformeigenen Screenreader jeweils einen vollständig ausgebauten Barrierefreiheitsbaum zur Verfügung — und die beiden Bäume sind sich jenseits der grundlegenden Label-und-Rolle-Ebene in nichts einig. Erfasst wurden alle Primitive, die VoiceOver und TalkBack im Jahr 2026 tatsächlich auswerten, die Art und Weise, wie React Native, Flutter und Kotlin Multiplatform versuchen, sie zu überbrücken, sowie der Punkt, an dem die Barrierefreiheit von mobilen WebViews leise von der Klippe fällt.
1. iOS UIAccessibility — Labels, Traits, Hints, Values
Jedes sichtbare Element auf einem iOS-Bildschirm hat oder kann eine Barrierefreiheitsrepräsentation besitzen. Apple stellt diese Repräsentation über das informelle Protokoll UIAccessibility bereit, das von UIView und jedem System-Control implementiert wird, sowie über UIAccessibilityElement — eine schlanke Klasse, die für Bereiche der Oberfläche allokiert wird, die gezeichnet, aber keine eigenständigen Views sind: Zeichen in einem selbst gezeichneten Diagramm, Glyphen in einer Core-Graphics-Szene, Bereiche innerhalb eines CALayer. VoiceOver, Switch Control, Full Keyboard Access und Voice Control konsumieren dasselbe Protokoll; wer es einmal versteht, beherrscht damit vier assistive Technologien.
Das Protokoll legt vier Primitive offen, die für fast jeden Bildschirm relevant sind. Das Barrierefreiheits-Label ist der kurze, menschenlesbare Name des Elements — „Senden“, „Profilfoto von Asha“, „Zurück“. Die Barrierefreiheits-Traits sind eine Bitmask aus rollenähnlichen Flags — .button, .header, .image, .selected, .adjustable, .staticText, .updatesFrequently — die VoiceOver mitteilen, wie es sich gegenüber dem Element verhalten und welche Gesten es aktivieren soll. Der Barrierefreiheits-Value ist eine String-Repräsentation des aktuellen Zustands („Ein“, „75 %“, „Donnerstag, 22. Mai“). Der Barrierefreiheits-Hint ist die längere, optionale Erläuterung („Doppeltippen, um den Foto-Viewer zu öffnen“), die VoiceOver nach einer Verzögerung vorliest, wenn der Nutzer nach dem Label allein nicht handelt.
Die vier Primitive ergänzen sich. Ein Schalter wird als Label + Trait + Value vorgelesen: „WLAN, Schalter-Taste, Ein“. Ein Schieberegler als Label + Trait + Value + Hint: „Lautstärke, einstellbar, 60 Prozent, nach oben oder unten wischen zum Anpassen“. Ein selbst gezeichneter Diagrammbalken wird als Kette von UIAccessibilityElements abgebildet, jedes mit einem Label, einem Value und einem Frame innerhalb seines Containers. Die Kette ist die API-Oberfläche — VoiceOver durchläuft sie linear, wenn der Nutzer nach rechts wischt, und respektiert die Reihenfolge, in der die Elemente über das accessibilityElements-Array des Containers publiziert werden.
Die View-Modifier .accessibilityLabel(), .accessibilityValue(), .accessibilityHint() und .accessibilityAddTraits() in SwiftUI werden auf dieselben UIAccessibility-Eigenschaften des darunterliegenden UIView kompiliert. SwiftUI fügt zusätzlich .accessibilityElement(children:) hinzu, das das „Zeichen-im-Diagramm“-Problem deklarativer löst als der UIKit-Ansatz — der Laufzeitvertrag, den VoiceOver sieht, ist jedoch identisch. Die UIKit-Namen zu kennen lohnt sich dennoch, denn jedes Apple-Beispiel, jede Stack-Overflow-Antwort und jedes Barrierefreiheits-Audit spricht in diesen Begriffen.
2. Android AccessibilityNodeInfo — Roles, Actions, importantForAccessibility
Android geht einen anderen Weg. Während iOS Barrierefreiheit als flaches Protokoll an jedem View verankert, serialisiert Android den gesamten Barrierefreiheitsbaum als Graph von AccessibilityNodeInfo-Objekten, von denen jedes eine Momentaufnahme eines Views zum Zeitpunkt einer TalkBack-Abfrage darstellt. Das Framework konstruiert die Momentaufnahmen verzögert; ein View veröffentlicht seinen Knoten durch Überschreiben von onInitializeAccessibilityNodeInfo() (oder in Compose durch Setzen von Semantics-Modifiern), und die Plattform verbindet die Eltern-Kind-Beziehungen zu einem Baum, der die View-Hierarchie widerspiegelt.
Die Primitive unterscheiden sich von iOS in drei wesentlichen Punkten. Erstens legt Android eine Rolle über ein string-typisiertes className-Feld offen — android.widget.Button, android.widget.CheckBox, android.widget.EditText. TalkBack liest den Klassennamen und entscheidet, wie er ankündigt („Schaltfläche“, „Kontrollkästchen“, „Eingabefeld“). Compose übersetzt seine Role.Button-, Role.Checkbox- und Role.RadioButton-Semantik in dasselbe Feld. Die Rolle ist granularer als eine iOS-Trait-Bitmask, aber auch starrer — eine vollständig benutzerdefinierte Rolle gibt es nicht, es sei denn, man akzeptiert die Ankündigung als „View“.
Zweitens stellt Android Interaktivität als Menge von Actions dar, die dem Knoten beigefügt sind: ACTION_CLICK, ACTION_LONG_CLICK, ACTION_SCROLL_FORWARD, ACTION_SET_TEXT, ACTION_FOCUS sowie eine lange Liste benutzerdefinierter Actions, die mit AccessibilityNodeInfo.AccessibilityAction registriert werden können. TalkBack zeigt die benutzerdefinierten Actions über den „Actions“-Rotor an — der Nutzer wischt mit einem Finger nach oben und hört jede benutzerdefinierte Action namentlich. iOS kennt dasselbe Konzept (UIAccessibilityCustomAction), doch auf Android ist die Action-Liste die Oberfläche; auf iOS ist es das Gestenvokabular.
Drittens besitzt Android importantForAccessibility, ein View-weises Enum (auto, yes, no, noHideDescendants), das steuert, ob der Knoten überhaupt im Baum erscheint. noHideDescendants ist das mächtigste Werkzeug in der Android-Barrierefreiheit und das am häufigsten vergessene — es entfernt den gesamten Teilbaum aus der TalkBack-Traversierung, gleichbedeutend mit aria-hidden=“true” im Web. iOS hat kein exaktes Gegenstück; das Nächste ist, accessibilityElements am Container auf ein leeres Array zu setzen, was jedoch nur die direkten Kinder des Containers entfernt, nicht den gesamten Teilbaum.
Android stellt ViewCompat.setAccessibilityLiveRegion() mit drei Werten bereit: none, polite, assertive. Das Vokabular spiegelt ARIA — fast. TalkBack respektiert die Höflichkeitsstufen zuverlässig. iOS hat auf Protokollebene kein Gegenstück: Aktualisierungen werden durch den Aufruf von UIAccessibility.post(notification: .announcement, argument: “Gespeichert”) angekündigt, einem imperativen Einmalaufruf, der nicht an einen View gebunden ist. Plattformübergreifende Brücken müssen eine dieser Methoden auf der anderen nachbilden, und die Impedanzfehlanpassung zeigt sich in jedem in Abschnitt 3 besprochenen Framework.
3. Plattformübergreifende Brücken — React Native, Flutter, Kotlin Multiplatform
Jedes plattformübergreifende mobile Framework muss die beiden oben beschriebenen APIs nehmen und eine einheitliche, framework-eigene Oberfläche daraus bauen. Keines gelingt das vollständig. Die drei dominierenden Ansätze im Jahr 2026 — React Native, Flutter und Kotlin Multiplatform mit Compose Multiplatform — sind jeweils ein etwas anderer Kompromiss zwischen Abstraktionsleckage und Abstraktion.
accessibilityLabel, accessibilityHint, accessibilityRole, accessibilityState auf Pressable und View bilden fast 1:1 auf UIAccessibility ab — aber die Rollennamen sind das React-Native-Vokabular, nicht das iOS-Vokabular.accessibilityRole=“button” setzt className auf android.widget.Button.accessibilityLiveRegion ist nur Android-seitig wirksam — auf iOS tut es stillschweigend nichts, und AccessibilityInfo.announceForAccessibility() muss manuell aufgerufen werden.UIAccessibilityElement-Instanzen auf dem Flutter-View übersetzt, wobei Traits aus den SemanticsAction- und SemanticsFlag-Mengen abgeleitet werden.SemanticsFlag.isLiveRegion.Modifier.semantics { } definiert Rollen und Actions einmal; jedes Zielsystem übersetzt denselben Semantics-Block in seine eigene native Barrierefreiheits-API.Das Muster ist bei allen dreien gleich: ein synthetischer, framework-geformter Semantikbaum auf der einen Seite, zwei plattformgeformte Barrierefreiheitsbäume auf der anderen, und ein Übersetzer dazwischen, der die einfachen Fälle gut und die komplexen mit spürbarem Qualitätsverlust behandelt. Einfache Fälle — ein Button mit einem Label, ein Bild mit Alternativtext, eine Überschrift — werden verlustfrei übersetzt. Komplexe Fälle — eine benutzerdefinierte Geste mit Zwei-Finger-Wischen, ein Diagramm, dessen Elemente eine fokussierbare Gruppe sein sollten, eine Live-Region, die auf iOS ohne eine View-gebundene Höflichkeitseinstellung feuern muss — lassen das Vokabular der darunterliegenden Plattform in den plattformübergreifenden Code durchsickern oder scheitern schlicht an der Übersetzung.
„Die ersten 80 Prozent mobiler Barrierefreiheit sind über alle Frameworks hinweg identisch. Die letzten 20 Prozent sind der Punkt, an dem jedes Framework verrät, in welcher nativen API es heimlich denkt.“
4. Die WebView-Lücke — wo mobile Web-Barrierefreiheit leise scheitert
Sowohl iOS als auch Android rendern Webinhalte über einen System-WebView — WKWebView auf iOS, android.webkit.WebView (oder Chrome Custom Tabs) auf Android. In beiden Fällen ist der WebView aus Sicht der Host-App eine Black Box: Die App sieht einen einzelnen View, doch der Screenreader sieht den gesamten DOM-Barrierefreiheitsbaum innerhalb des WebViews. Die Brücke zwischen den beiden Bäumen ist der Ort, an dem überraschend viel mobile Web-Barrierefreiheit still schiefläuft.
Der Mechanismus ist, auf den ersten Blick, unkompliziert. Wenn der Fokus eines Screenreaders in einen WebView eintritt, liest die Plattform den Barrierefreiheitsbaum des Dokuments direkt aus der Browser-Engine — WebKit auf iOS, Blink auf Android — und traversiert ihn als Teilbaum des Host-App-Baums. Die Rollen, Labels und ARIA-Attribute des Webs werden in Echtzeit in das Vokabular der Plattform übersetzt. Ein button-Element ohne explizite Rolle im WebView wird auf beiden Plattformen als Schaltfläche vorgelesen; eine aria-live=“polite”-Region wird auf beiden korrekt angekündigt; ein aria-label auf einem Link erscheint als Barrierefreiheits-Label des Links. In den ersten drei Jahren mobiler Web-Nutzung funktionierte das einfach.
Die Klippe zeigt sich an drei Stellen. Erstens sind benutzerdefinierte Gesten, die in der Host-App definiert sind — ein Zwei-Finger-Wischen zum Schließen, ein Magic-Tap zum Starten und Pausieren — für den Inhalt des WebViews unsichtbar; sie feuern auf das falsche Ziel oder gar nicht, wenn der Fokus im Dokument liegt. Zweitens konkurrieren UIAccessibilityElements der Host-App, die über dem WebView gezeichnet werden (ein Floating-Action-Button, eine benutzerdefinierte Toolbar), mit dem Baum des WebViews um die Traversierungsreihenfolge, und die resultierende Lesereihenfolge ist über iOS-Versionen hinweg nicht deterministisch. Drittens — und das ist der bei weitem größte einzelne Fehlerfall in der mobilen Web-Barrierefreiheit — respektiert der WebView auf iOS die aria-live-Höflichkeitsstufen nicht so, wie Safari es in einem Tab tut: Die Ankündigungs-Pipeline von WKWebView verwirft die Unterscheidung zwischen polite und assertive, sodass jede Live-Aktualisierung unabhängig vom Markup als polite behandelt wird.
<div role="alert" aria-live="assertive">
Connection lost — retrying.
</div>VoiceOver in einem normalen Safari-Tab unterbricht die aktuelle Ausgabe und spricht die Meldung sofort. Die assertive-Höflichkeitsstufe wird von Anfang bis Ende durch WebKit eingehalten.
<div role="alert" aria-live="assertive">
Connection lost — retrying.
</div>Gleicher Quelltext, gleiche Browser-Engine — doch die Barrierefreiheitsbrücke des WKWebView zu UIKit stuft die Ankündigung auf eine verzögerte polite-Meldung zurück. Der Nutzer hört sie mit Verzögerung, manchmal nachdem er bereits in das nun defekte Formular getippt hat.
Für Ankündigungen innerhalb eines WebViews ist das einzige zuverlässige plattformübergreifende Muster im Jahr 2026, eine JavaScript-Brücke in die Host-App zu exponieren — einen kleinen postMessage-Handler — und assertive Ankündigungen aus dem DOM, in die Host-App und zurück durch UIAccessibility.post(notification: .announcement, …) auf iOS oder announceForAccessibility() auf Android zu leiten. Das aria-live des Webs überlebt nur für echte polite-Meldungen, bei denen eine Latenz von wenigen Sekunden akzeptabel ist.
5. Die Vergleichstabelle — was womit korrespondiert
Erfasst wurden 28 Primitive, die VoiceOver und TalkBack in der Praxis tatsächlich auswerten — die Vereinigungsmenge der iOS-UIAccessibility-Protokolloberfläche, der Android-AccessibilityNodeInfo-Oberfläche und der meistgenutzten React-Native- und Flutter-Props. Die Tabelle zeigt nur die strittigen Zeilen: Primitive, bei denen die Abbildung unvollständig, asymmetrisch oder überraschend ist. Zeilen mit sauberer Abbildung (Label, Button-Rolle, Image-Rolle, Überschrift) wurden aus Platzgründen weggelassen.
| Fähigkeit | iOS UIAccessibility | Android AccessibilityNodeInfo | React Native 0.76 | Flutter 3.27 |
|---|---|---|---|---|
| Hint-Text (längere Erläuterung) | accessibilityHint | tooltipText (API 28+) | accessibilityHint (nur iOS) | SemanticsProperties.hint |
| Live-Region-Höflichkeit | N/A — nur imperativer Post | setAccessibilityLiveRegion() | accessibilityLiveRegion (nur Android) | SemanticsFlag.isLiveRegion |
| Teilbaum vor a11y verbergen | accessibilityElementsHidden (nur direkte Kinder) | importantForAccessibility=“noHideDescendants” | accessibilityElementsHidden / importantForAccessibility | ExcludeSemantics-Widget |
| Benutzerdefinierte Action (Rotor / Menü) | UIAccessibilityCustomAction | AccessibilityNodeInfo.AccessibilityAction | accessibilityActions + onAccessibilityAction | SemanticsAction mit benutzerdefiniertem Label |
| Adjustable / Slider-Semantik | UIAccessibilityTraitAdjustable + accessibilityIncrement | RangeInfo + ACTION_SCROLL_FORWARD | accessibilityRole=“adjustable” + Handler | Slider stellt SemanticsAction.increase bereit |
| Überschriftsebene | UIAccessibilityTraitHeader (ohne Ebene) | setHeading(true) (ohne Ebene) | accessibilityRole=“header” (ohne Ebene) | SemanticsProperties.headingLevel (1–6) |
| Ausgewählt / aktivierter Zustand | UIAccessibilityTraitSelected | setSelected(true) + setCheckable() | accessibilityState={selected, checked} | SemanticsFlag.isSelected |
| Gruppen- / Container-Semantik | shouldGroupAccessibilityChildren | setScreenReaderFocusable(true) | accessible={true} am Elternelement | MergeSemantics-Widget |
| Einmalige Meldung ankündigen | UIAccessibility.post(.announcement, …) | view.announceForAccessibility() | AccessibilityInfo.announceForAccessibility() | SemanticsService.announce() |
Drei Muster stechen aus der Tabelle hervor. Erstens ist die Asymmetrie bei Live-Regionen die wichtigste Quelle plattformübergreifender Divergenz — Android hat eine View-gebundene Höflichkeitseinstellung, iOS nur einen globalen imperativen Post, und jedes oben genannte Framework ist gezwungen, den Unterschied zu verschleiern. Zweitens sind Überschriftsebenen der einzige Punkt, an dem Flutter beide nativen Plattformen tatsächlich übertrifft; die iOS- und Android-Primitive wissen nur, „dies ist eine Überschrift“, nicht „dies ist ein H3 unter einem H2“. Drittens ist das Primitiv zum Verbergen vor der Barrierefreiheit auf Android flexibler als auf iOS — noHideDescendants verbirgt einen gesamten Teilbaum in einem Schritt, während iOS verlangt, die Kinder jedes Containers einzeln zu verbergen.
6. Das mobile-native Playbook
Zuerst das native Vokabular lernen, dann das Framework-Vokabular
Jede plattformübergreifende Brücke — React Native, Flutter, Compose Multiplatform — hat ihre eigene Benennung für Barrierefreiheits-Props, und jede dieser Benennungen ist eine leichte Unwahrheit darüber, was die darunterliegende Plattform tatsächlich tut. Wenn ein Screenreader nicht korrekt ankündigt, liegt der Fehler fast immer in der nativen API, in die das Framework übersetzt hat, nicht im Framework-Prop, das gesetzt wurde. Die UIAccessibility-Dokumentation und die AccessibilityNodeInfo-Dokumentation sollten mindestens einmal gelesen werden; die Framework-Dokumentation ergibt danach erst Sinn.
Live-Ankündigungen speziell auf iOS testen
Die Live-Region-Asymmetrie aus Abschnitt 2 bedeutet, dass jeder Code, der davon ausgeht, aria-live=“assertive” oder accessibilityLiveRegion=“assertive” funktioniere, auf iOS still degradiert. Es empfiehlt sich, vor dem Ausliefern eines Features, dessen UX davon abhängt, dass der Nutzer eine Zustandsänderung hört, ein kleines Testgerüst aufzubauen, das sowohl eine polite als auch eine assertive Ankündigung auf beiden Plattformen auslöst — mit VoiceOver und TalkBack auf echten Geräten.
Für alles Assertive aus WebViews heraus brücken
Die WKWebView-Rückstufung assertiver Ankündigungen ist kein Fehler, den Apple in absehbarer Zeit behebt — sie ist in jeder iOS-Version ab 14 aufwärts gleich geblieben. Bei einer Hybrid-App, in der der Nutzer einem schwerwiegenden Fehler innerhalb eines WebViews begegnen kann, sollte die Ankündigung über eine JS-Brücke zur Host-App geleitet und dort als plattformeigene Ankündigung abgesetzt werden. Das Web allein reicht nicht.
Das „Merge“- oder „Group“-Semantik des Frameworks nutzen, nicht Element für Element
Sowohl iOS (shouldGroupAccessibilityChildren), Android (setScreenReaderFocusable) als auch Flutter (MergeSemantics) bieten eine Möglichkeit, ein visuelles Cluster — ein Icon plus ein Label plus ein Value — zu einem einzigen Barrierefreiheitselement zusammenzufassen. Diese Möglichkeit sollte genutzt werden. Das Standard-Verhalten „jedes Blatt ist ein fokussierbares Element“ verwandelt einen Navigations-Chip mit sechs Elementen in sechs VoiceOver-Wischgesten.
Prüfen mit Accessibility Inspector und TalkBack-Entwicklereinstellungen
Beide Plattformen liefern einen kostenlosen, offiziellen Inspector für den Live-Barrierefreiheitsbaum mit — den Accessibility Inspector auf macOS (gekoppelt mit dem verbundenen iOS-Simulator oder Gerät) sowie das Overlay „Barrierefreiheitsfokus anzeigen“ und die „Entwicklereinstellungen“ auf Android. Diese Werkzeuge ermöglichen es, den eigenen App-Baum so zu lesen, wie der Screenreader ihn sieht. Es darf nicht davon ausgegangen werden, dass das Debug-Logging des Frameworks dasselbe zeigt, was die Plattform TalkBack präsentiert.
Fazit: Das Framework ist nachgelagert zur Plattform
Es ist verlockend zu glauben — und die Framework-Dokumentation fördert diesen Glauben —, dass eine plattformübergreifende Barrierefreiheits-API eine einheitliche Abstraktion über zwei gleichwertige native APIs darstellt. Die Vergleichstabelle in Abschnitt 5 widerlegt die Vereinheitlichung. Die beiden nativen APIs wurden unabhängig voneinander, von zwei verschiedenen Teams, auf der Grundlage zweier unterschiedlicher mentaler Modelle dafür entwickelt, wie der Screenreader ein Dokument traversieren soll; die Unterschiede sind real, sie dringen durch jedes Framework hindurch, und die Leckage zeigt sich in den Teilen der Nutzererfahrung, die am wichtigsten sind — Live-Aktualisierungen, benutzerdefinierte Gesten, verborgene Teilbäume, Überschriftshierarchien.
Die gute Nachricht nach diesem Absatz: Die Grundlagen funktionieren. Ein Button mit einem Label, ein Bild mit Alternativtext, eine Überschrift am Anfang eines Abschnitts — diese Primitive werden durch jedes Framework verlustfrei übertragen und auf beiden Plattformen korrekt angekündigt. Wer nur diese Primitive liefert, muss nicht über UIAccessibility oder AccessibilityNodeInfo nachdenken; die Framework-Standards sind ehrlich. Die Schwierigkeiten beginnen, wenn die Benutzeroberfläche etwas Interessantes tut — was zugleich der Moment ist, in dem Barrierefreiheit am meisten zählt.
Das Playbook in Abschnitt 6 ist die kürzeste Version des Arguments, die die meisten Menschen mit Behinderungen zu einer funktionierenden Erfahrung führt: zuerst in nativen Primitiven denken, auf echten Geräten auf beiden Plattformen testen, für assertive Meldungen aus WebViews heraus brücken, Blattknoten bewusst gruppieren und die offiziellen Inspectors verwenden. Das gewählte Framework hilft mit den ersten 80 Prozent und tritt für die letzten 20 Prozent beiseite. In diesen letzten 20 Prozent lebt der Screenreader-Nutzer.
„VoiceOver und TalkBack lesen zwei verschiedene Dokumente aus demselben Quellcode. Ob der Nutzer den Unterschied bemerkt, ist ein Maß dafür, wie gut die darunterliegende Plattform unter dem Framework verstanden wurde.“