Mobiilialustan saavutettavuusrajapinnat vuonna 2026:
UIAccessibility, AccessibilityNode ja verkko
iOS ja Android paljastavat molemmat täysin varustetun saavutettavuuspuun alustan ruudunlukuohjelmalle — eivätkä nämä kaksi puuta sovi yhteen muutoin kuin nimen ja roolin perusasioiden osalta. Kartoitimme jokaisen primitiivin, jota VoiceOver ja TalkBack todella käyttävät vuonna 2026, sekä tavan, jolla React Native, Flutter ja Kotlin Multiplatform pyrkivät siltaamaan ne, ja kohdan, jossa mobiiliverkon WebView-saavutettavuus hiljaa romahtaa.
1. iOS UIAccessibility — nimet, ominaispiirteet, vihjeet, arvot
Jokaisella näkyvällä elementillä iOS-näytöllä on tai voi olla saavutettavuusesitys. Apple toimittaa tämän esityksen UIAccessibility-epävirallisen protokollan kautta, jonka toteuttavat UIView ja jokainen järjestelmäohjain, sekä UIAccessibilityElement-kevyen luokan kautta, jonka varaat käyttöliittymäsi osille, jotka piirretään mutta eivät itsessään ole näkymiä — merkit omassa piirretyssä kaaviossa, glyfit Core Graphics -kohtauksessa, alueet CALayer-kerroksessa. VoiceOver, Switch Control, Full Keyboard Access ja Voice Control käyttävät kaikki samaa protokollaa; sen oppiminen kerralla antaa pääsyn neljään avustavaan teknologiaan.
Protokolla paljastaa neljä primitiiviä, jotka ovat tärkeitä lähes jokaisella näytöllä. Saavutettavuusnimi on lyhyt, ihmisluettava elementin nimi — “Lähetä”, “Asha-profiilikuva”, “Takaisin”. Saavutettavuusominaispiirteet ovat roolimaisten lippujen bittimaskeja — .button, .header, .image, .selected, .adjustable, .staticText, .updatesFrequently — jotka kertovat VoiceOverille, miten käyttäytyä elementin ympärillä ja mitkä eleet aktivoida. Saavutettavuusarvo on merkkijonoesitys nykyisestä tilasta (“Päällä”, “75 %”, “Torstai 22. toukokuuta”). Saavutettavuusvihje on pidempi, valinnainen selitys (“Kaksoisnapauta avataksesi kuvakatselu”), jonka VoiceOver lausuu viiveellä, jos käyttäjä ei toimi pelkän nimen perusteella.
Neljä primitiiviä muodostavat kokonaisuuden. Kytkin luetaan nimellä + ominaispiirteellä + arvolla: “Wi-Fi, kytkimen painike, Päällä”. Liukusäädin luetaan nimellä + ominaispiirteellä + arvolla + vihjeellä: “Äänenvoimakkuus, säädettävä, 60 prosenttia, pyyhkäise ylös tai alas säätääksesi”. Omaan piirretty kaavion palkki luetaan UIAccessibilityElementien ketjuna, joista jokaisella on nimi, arvo ja kehys säilössään. Ketju on API-pinta — VoiceOver kulkee sen lineaarisesti käyttäjän pyyhkäistessä oikealle ja kunnioittaa järjestystä, jossa elementit julkaistaan säilön accessibilityElements-taulukon kautta.
SwiftUI:n .accessibilityLabel()-, .accessibilityValue()-, .accessibilityHint()- ja .accessibilityAddTraits()-näkymämuokkaajat kääntyvät samoiksi UIAccessibility-ominaisuuksiksi taustalla olevassa UIView:ssa. SwiftUI lisää myös .accessibilityElement(children:)-muokkaajan, joka ratkaisee “merkit kaaviossa” -ongelman deklaratiivisemmin kuin UIKit-lähestymistapa — mutta VoiceOverin näkemä ajonaikainen sopimus on identtinen. UIKit-nimien opettelu on edelleen aikasi arvoista, koska jokainen Applen näyte, jokainen Stack Overflow -vastaus ja jokainen saavutettavuusauditointi puhuu niillä.
2. Android AccessibilityNodeInfo — roolit, toiminnot, importantForAccessibility
Android ottaa eri reitin. Missä iOS ripustaa saavutettavuuden tasaiseen protokollaan jokaisessa näkymässä, Android serialisoi koko saavutettavuuspuun AccessibilityNodeInfo-objektien graafina, joista jokainen on tilannekuva näkymästä TalkBack-kyselyn saapuessa. Kehys rakentaa tilannekuvat laiskasti; View julkaisee solmunsa ylikirjoittamalla onInitializeAccessibilityNodeInfo():n (tai Composessa asettamalla semantiikkamuokkaajia), ja alusta ompelee vanhempi-lapsi-suhteet puuksi, joka peilaa näkymähierarkiaa.
Primitiivit eroavat iOS:sta kolmella merkittävällä tavalla. Ensinnäkin Android paljastaa roolin merkkijonomuotoisen className-kentän kautta — android.widget.Button, android.widget.CheckBox, android.widget.EditText. TalkBack lukee luokkanimen ja päättää, miten ilmoittaa (“painike”, “valintaruutu”, “muokkauskenttä”). Compose kääntää Role.Button-, Role.Checkbox-, Role.RadioButton-semantiikkansa samaan kenttään. Rooli on yksityiskohtaisempi kuin iOS:n ominaispiirteiden bittimaskissa, mutta myös jäykempi — ei ole “täysin mukautettu” -roolia, ellei hyväksy ilmoitusta “näkymänä”.
Toiseksi Android esittää vuorovaikutteisuuden solmuun liitettyinä toimintoina: ACTION_CLICK, ACTION_LONG_CLICK, ACTION_SCROLL_FORWARD, ACTION_SET_TEXT, ACTION_FOCUS ja pitkä lista mukautettuja toimintoja, joita voi rekisteröidä AccessibilityNodeInfo.AccessibilityAction-luokan kautta. TalkBack näyttää mukautetut toiminnot “toiminnot”-pyörässä — käyttäjä pyyhkäisee ylös yhdellä sormella ja kuulee jokaisen mukautetun toiminnon nimeltä. iOS:lla on sama käsite (UIAccessibilityCustomAction), mutta Androidilla toimintoluettelo on pinta; iOS:lla ele-sanasto on.
Kolmanneksi Androidilla on importantForAccessibility, näkymäkohtainen luetteloarvo (auto, yes, no, noHideDescendants), joka hallitsee, näkyykö solmu puussa lainkaan. noHideDescendants on yksittäinen tehokkain työkalu Androidin saavutettavuudessa ja se, joka useimmin unohdetaan — se poistaa koko alipuun TalkBackin läpikulusta, vastaten verkon aria-hidden=“true”:ta. iOS:lla ei ole tarkkaa vastinetta; lähinnä on säätää accessibilityElements tyhjäksi taulukoksi säilössä, mikä poistaa vain säilön välittömät lapset, ei koko alipuuta.
Android paljastaa ViewCompat.setAccessibilityLiveRegion():n kolmella arvolla: none, polite, assertive. Sanasto peilaa ARIAa — melkein. TalkBack kunnioittaa kohteliaisuustasoja luotettavasti. iOS:lla ei ole mitään vastaavaa protokollatasolla: päivitykset ilmoitetaan kutsumalla UIAccessibility.post(notification: .announcement, argument: “Tallennettu”), imperatiivinen kertalaukaus, joka ei kiinnity näkymään. Alustojen välisten siltojen on väärennettävä toinen näistä toisen päälle, ja impedanssin epäsuhta näkyy jokaisessa osiossa 3 tarkastellussa kehyksessä.
3. Alustojen väliset sillat — React Native, Flutter, Kotlin Multiplatform
Jokaisen alustojen välisen mobiiliviitekehyksen on otettava yllä olevat kaksi rajapintaa ja esitettävä yksi, viitekehysmuotoinen pinta. Mikään niistä ei onnistu täysin. Kolme lähestymistapaa hallitsevat markkinoita vuonna 2026 — React Native, Flutter ja Kotlin Multiplatform Compose Multiplatformilla — jokainen hieman erilaisella kaupalla vuodon ja abstraktion välillä.
accessibilityLabel, accessibilityHint, accessibilityRole, accessibilityState Pressablessa ja Viewssa kartoittuvat lähes 1:1 UIAccessibilityyn — mutta roolinimet ovat React Nativen sanasto, eivät iOS:n.accessibilityRole=“button” asettaa className:ksi android.widget.Button.accessibilityLiveRegion-propri on vain Androidilla — iOS:lla se hiljaa ei tee mitään, ja sinun on kutsuttava AccessibilityInfo.announceForAccessibility() manuaalisesti.UIAccessibilityElement-instansseiksi Flutter-näkymässä, ominaispiirteet kartoitettuna SemanticsAction- ja SemanticsFlag-joukoista.SemanticsFlag.isLiveRegion:ksi.Modifier.semantics { } määrittelee roolit ja toiminnot kerran; jokainen kohde kääntää saman semantiikkalohkon omaan natiiviin saavutettavuusrajapintaansa.Kaikkien kolmen malli on sama: synteettinen, viitekehysmuotoinen semantiikkapuu yhdellä puolella, kaksi alustamuotoista saavutettavuuspuuta toisella ja kääntäjä välissä, joka käsittelee yksinkertaiset tapaukset hyvin ja monimutkaiset tapaukset havaittavalla tarkkuushäviöllä. Yksinkertaiset tapaukset — painike nimellä, kuva alt-tekstillä, otsikko — kulkevat läpi ilman häviötä. Monimutkaiset tapaukset — mukautettu ele kahdella sormipyyhkäisyllä, kaavio, jonka elementtien pitäisi olla kohdistettava ryhmä, live-alue, jonka on aktivoiduttava iOS:lla ilman näkymäsidonnaista kohteliaisuusasetusta — vuotavat taustalla olevan alustan sanaston alustojen väliseen koodiin tai yksinkertaisesti epäonnistuvat kääntymisessä.
”Ensimmäiset 80 prosenttia mobiilisaavutettavuudesta ovat identtiset jokaisessa viitekehyksessä. Viimeiset 20 prosenttia ovat se, missä jokainen viitekehys paljastaa, minkä natiivirajapin nan se salaa ajattelee.”
4. WebView-kuilu — missä mobiiliverkon saavutettavuus hiljaa pettää
Sekä iOS että Android renderöivät verkkosisällön järjestelmän WebView’n kautta — WKWebView iOS:lla, android.webkit.WebView (tai Chrome Custom Tabs) Androidilla. Kummassakin tapauksessa WebView on musta laatiikko isäntäsovelluksen näkökulmasta: sovellus näkee yhden näkymän, mutta ruudunlukuohjelma näkee koko DOM-saavutettavuuspuun sen sisällä. Silta näiden kahden puun välillä on paikka, jossa yllättävän suuri osa mobiilisaavutettavuudesta menee hiljaa pieleen.
Mekanismi on pintapuolisesti suoraviivainen. Kun ruudunlukuohjelman kohdistus siirtyy WebView’hun, alusta lukee dokumentin saavutettavuuspuun suoraan selainmoottorista — WebKit iOS:lla, Blink Androidilla — ja kulkee sen läpi isäntäsovelluksen puun alipuuna. Verkon roolit, nimet ja ARIA-attribuutit käännetään alustan sanastoon reaaliajassa. button-elementti ilman eksplisiittistä roolia WebView’n sisällä luetaan painikkeena molemmilla alustoilla; aria-live=“polite”-alue ilmoittaa oikein molemmilla; aria-label linkissä tulee esiin linkin saavutettavuusnimenä. Mobiiliverkon ensimmäiset kolme vuotta tämä vain toimi.
Kuilu ilmenee kolmessa kohdassa. Ensinnäkin isäntäsovelluksessa määritellyt mukautetut eleet — kaksisorminen pyyhkäisy sulkeaksesi, maaginen napautus toistaaksesi ja pysäyttääksesi — ovat näkymättömiä WebView’n sisällölle; ne aktivoituvat väärään kohteeseen tai eivät aktivoidu lainkaan, kun kohdistus on dokumentin sisällä. Toiseksi isäntäsovelluksen UIAccessibilityElementit, jotka piirretään WebView’n päälle (kelluva toimintopainike, mukautettu työkalupalkki), kilpailevat WebView’n puun kanssa läpikäyntijärjestyksestä, ja tuloksena oleva lukujärjestys on ei-deterministinen iOS-versioiden välillä. Kolmanneksi — ja tämä on yksittäin suurin vikamuoto mobiiliverkon saavutettavuudessa — iOS:n WebView ei kunnioita aria-live-kohteliaisuustasoja samoin kuin Safari tekee välilehdessä: WKWebView:n ilmoitusputkisto pudottaa polite- ja assertive-erottelun, joten jokainen live-päivitys käsitellään politena riippumatta merkinnästä.
<div role="alert" aria-live="assertive">
Yhteys katkesi — yritetään uudelleen.
</div>VoiceOver normaalissa Safari-välilehdessä keskeyttää nykyisen lausuman ja puhuu viestin heti. assertive-kohteliaisuus kunnioitetaan päästä päähän WebKitin kautta.
<div role="alert" aria-live="assertive">
Yhteys katkesi — yritetään uudelleen.
</div>Sama merkintä, sama selainmoottori — mutta WKWebView:n saavutettavuussilta UIKitiin alentaa ilmoituksen lykätyksi polite-viestiksi. Käyttäjä kuulee sen viiveellä, joskus sen jälkeen kun on jo kirjoittanut nyt-rikkinäiseen lomakkeeseen.
WebView’n sisällä tapahtuviin ilmoituksiin ainoa luotettava alustojen välinen malli vuonna 2026 on tarjota JavaScript-silta isäntäsovellukseen — pieni postMessage-käsittelijä — ja reititttää assertiiviset ilmoitukset DOM:ista isäntäsovellukseen ja takaisin UIAccessibility.post(notification: .announcement, …):n kautta iOS:lla tai announceForAccessibility():n kautta Androidilla. Verkon aria-live kestää vain aidosti kohteliaisiin viesteihin, joissa muutaman sekunnin viive on hyväksyttävä.
5. Vastaavuustaulukko — mikä vastaa mitä
Kartoitimme 28 primitiiviä, joita VoiceOver ja TalkBack todella käyttävät käytännössä — iOS UIAccessibility-protokollapinnan, Android AccessibilityNodeInfo-pinnan ja yleisimmin käytettyjen React Native- ja Flutter-alustojen välisten proppien unioni. Alla oleva taulukko kattaa vain kiistanalaiset rivit: primitiivit, joissa kartoitus on puutteellinen, epäsymmetrinen tai yllättävä. Rivit, joissa kartoitus on puhdas (nimi, painikeroolii, kuvarooli, otsikko), on jätetty pois pituussyistä.
| Ominaisuus | iOS UIAccessibility | Android AccessibilityNodeInfo | React Native 0.76 | Flutter 3.27 |
|---|---|---|---|---|
| Vihjeteksti (pidempi selitys) | accessibilityHint | tooltipText (API 28+) | accessibilityHint (vain iOS) | SemanticsProperties.hint |
| Live-alueen kohteliaisuus | Ei saatavilla — vain imperatiivinen post | setAccessibilityLiveRegion() | accessibilityLiveRegion (vain Android) | SemanticsFlag.isLiveRegion |
| Piilota alipuu saavutettavuudelta | accessibilityElementsHidden (vain lapset) | importantForAccessibility=“noHideDescendants” | accessibilityElementsHidden / importantForAccessibility | ExcludeSemantics-widget |
| Mukautettu toiminto (pyörä / valikko) | UIAccessibilityCustomAction | AccessibilityNodeInfo.AccessibilityAction | accessibilityActions + onAccessibilityAction | SemanticsAction mukautetulla nimellä |
| Säädettävä / liukusäädintoiminto | UIAccessibilityTraitAdjustable + accessibilityIncrement | RangeInfo + ACTION_SCROLL_FORWARD | accessibilityRole=“adjustable” + käsittelijät | Slider paljastaa SemanticsAction.increase |
| Otsikkotaso | UIAccessibilityTraitHeader (ei tasoa) | setHeading(true) (ei tasoa) | accessibilityRole=“header” (ei tasoa) | SemanticsProperties.headingLevel (1–6) |
| Valittu / vaihdettu tila | UIAccessibilityTraitSelected | setSelected(true) + setCheckable() | accessibilityState={selected, checked} | SemanticsFlag.isSelected |
| Ryhmä / säilön semantiikka | shouldGroupAccessibilityChildren | setScreenReaderFocusable(true) | accessible={true} vanhemmassa | MergeSemantics-widget |
| Ilmoita kertaluonteinen viesti | UIAccessibility.post(.announcement, …) | view.announceForAccessibility() | AccessibilityInfo.announceForAccessibility() | SemanticsService.announce() |
Taulukosta erottuu kolme kuviota. Ensinnäkin epäsymmetria live-alueiden ympärillä on yksittäin suurin alustojen välisen eroavuuden lähde — Androidilla on näkymäkohtainen kohteliaisuusasetus, iOS:lla on vain globaali imperatiivinen post, ja jokainen yllä oleva viitekehys on pakotettu valehtelemaan erosta. Toiseksi otsikkotasot ovat ainoa kohta, jossa Flutter aidosti parantaa molempia natiivialustoja; iOS:n ja Androidin primitiivit tietävät vain “tämä on otsikko”, eivät “tämä on H3 H2:n alla”. Kolmanneksi “piilota saavutettavuudelta” -primitiivi on joustavampi Androidilla kuin iOS:lla — noHideDescendants piilottaa koko alipuun yhdellä liikkeellä, kun taas iOS vaatii jokaisen säilön lasten piilottamista erikseen.
6. Mobiilialustan käsikirja
Opi natiivisanasto ennen viitekehyksen sanastoa
Jokaisella alustojen välisellä sillalla — React Native, Flutter, Compose Multiplatform — on oma nimensä saavutettavuuspropriille, ja jokainen näistä nimistä on pieni valhe siitä, mitä taustalla oleva alusta todella tekee. Kun ruudunlukuohjelma ei ilmoita oikein, vika elää lähes aina natiivirajapinnassa, johon viitekehys käänsi, eikä viitekehyksen proprissa jonka asetit. Lue UIAccessibility-dokumentaatio ja AccessibilityNodeInfo-dokumentaatio vähintään kerran; viitekehyksen dokumentaatio on järkevä vasta sen jälkeen.
Testaa live-ilmoitukset erityisesti iOS:lla
Osion 2 live-alueen epäsymmetria tarkoittaa, että koodi, joka olettaa aria-live=“assertive”:n tai accessibilityLiveRegion=“assertive”:n toimivan, tulee hiljaa heikentymään iOS:lla. Rakenna pieni testivaljastus, joka laukaisee sekä kohteliaan että assertiivisen ilmoituksen molemmilla alustoilla, VoiceOverilla ja TalkBackilla oikeilla laitteilla, ennen kuin toimitat ominaisuuden, jonka käyttökokemus riippuu käyttäjän kuulemasta tilamuutoksesta.
Sillat WebView’sta ulos assertiivisille ilmoituksille
WKWebView:n assertiivisten ilmoitusten alennus ei ole vika, jonka Apple korjaa pian — se on ollut sama kaikissa iOS-versioissa 14:stä lähtien. Jos toimitat hybridiappin, jossa käyttäjä voi kohdata kohtalokkaan virheen WebView’n sisällä, reititä ilmoitus JS-sillan kautta isäntäsovellukseen ja anna isäntäsovelluksen laukaista alustan ilmoitus. Verkko yksinään ei riitä.
Käytä viitekehyksen “yhdistä” tai “ryhmitä” -semantiikkaa, ei lapsi kerrallaan
Sekä iOS (shouldGroupAccessibilityChildren), Android (setScreenReaderFocusable) että Flutter (MergeSemantics) tarjoavat tavan tiivistää visuaalinen ryväs — kuvake plus nimi plus arvo — yhdeksi saavutettavuuselementiksi. Käytä sitä. Oletuskäyttäytyminen, jossa “jokainen lehtisolmu on kohdistettava elementti”, muuttaa kuusielementtisen navigaatiosirun kuudeksi VoiceOver-pyyhkäisyksi.
Auditoi Accessibility Inspectorilla ja TalkBack Developer Settingsillä
Molemmat alustat toimittavat ilmaisen virallisen tarkastimen live-saavutettavuuspuulle — Accessibility Inspector macOS:lla (pariutettuna yhdistettyyn iOS-simulaattoriin tai -laitteeseen) ja “Näytä saavutettavuuskohdistus” plus “Kehittäjäasetukset” -kerros Androidilla. Käytä niitä lukemaan oman sovelluksesi puu samoin kuin ruudunlukuohjelma sen näkee; älä oleta, että viitekehyksen virheenkorjausloki näyttää saman asian kuin alusta näyttää TalkBackille.
Johtopäätös: viitekehys on alustan alapuolella
On houkuttelevaa uskoa — ja viitekehysdokumentaatio rohkaisee tähän uskomukseen — että alustojen välinen saavutettavuusrajapinta on yksi yhtenäinen abstraktio kahden vastaavan natiivirajapin nan päälle. Osion 5 vastaavuustaulukko kumoaa yhtenäistämisen. Kaksi natiivirajapin taa suunniteltiin itsenäisesti, kahden eri tiimin toimesta, kahden eri käsitemallin ympärille siitä, miten ruudunlukuohjelman pitäisi kulkea dokumentin läpi; erot ovat todellisia, ne vuotavat jokaisen viitekehyksen läpi ja vuoto näkyy käyttäjäkokemuksen tärkeimmissä osissa — live-päivitykset, mukautetut eleet, piilotetut alipuut, otsikkohierarkiat.
Hyvä uutinen sen jälkeen: perusteet toimivat. Painike nimellä, kuva alt-tekstillä, otsikko osion alussa — nämä kulkevat läpi jokaisen viitekehyksen ja ilmoitetaan oikein molemmilla alustoilla. Jos toimitat vain näitä primitiivejä, sinun ei tarvitse ajatella UIAccessibilitya tai AccessibilityNodeInfota; viitekehyksen oletukset ovat rehellisiä. Ongelmat alkavat, kun käyttöliittymä alkaa tehdä jotain mielenkiintoista — joka on myös se hetki, kun saavutettavuus alkaa eniten merkitä.
Osion 6 käsikirja on lyhin versio argumentista, joka vie eniten vammaisia käyttäjiä toimivaan kokemukseen: ajattele ensin natiiviprimitiivejä, testaa oikeilla laitteilla molemmilla alustoilla, sillat ulos WebView’sta kun se on tarkoituksellista, ryhmittele lehtisolmut harkitusti ja käytä virallisia tarkastimia. Valitsemasi viitekehys auttaa ensimmäisen 80 prosentin kanssa ja väistyy viimeisen 20 prosentin edessä. Tässä viimeisessä 20 prosentissa ruudunlukuohjelman käyttäjä elää.
”VoiceOver ja TalkBack lukevat kahta eri dokumenttia samasta lähdekoodista. Se, huomaako käyttäjä eron, on mittari sille, kuinka hyvin ymmärsit viitekehyksesi alla olevan alustan.”