Dostępność w iOS 27: aplikacje do czytania i kontrolki niestandardowe
Czytanie obszernych treści to inny problem niż poruszanie się po interfejsie: chodzi o płynne przemieszczanie się przez tekst, a nie przeskakiwanie między kontrolkami. Dwie sesje o dostępności z WWDC26 rozdzielają się dokładnie wzdłuż tej granicy: jedna dotyczy powierzchni do czytania, druga — kontrolek, które ją otaczają.
Ten podział ma znaczenie, ponieważ poprawki różnią się rodzajem. Usterki aplikacji do czytania dotyczą ciągłości: tekst, który nie łączy się między akapitami, czy odczyt całości zatrzymujący się na dole strony. Usterki kontrolki niestandardowej dotyczą przekładu: gest, który wszystko przekazuje wizualnie, a VoiceOverowi nic. iOS 27 dostarcza API wymierzone w oba te problemy, a jedno z nich, accessibilityLinkedGroup, jest w tym roku nowością.
TL;DR
- Aplikacje do czytania powinny najpierw sięgnąć po systemowe widoki tekstu.
UITextView,TextEditororazTextz włączonym zaznaczaniem przyjmują protokółUITextInputi bez dodatkowej pracy otrzymują nawigację po wierszu, słowie i znaku oraz zaznaczanie1. - Gdy układ wymusza rozdzielenie elementów tekstowych, należy je połączyć, aby VoiceOver mógł przekroczyć szew. iOS 18 wprowadził
accessibilityNextTextNavigationElement/accessibilityPreviousTextNavigationElement; iOS 27 dodaje dla tego samego efektu modyfikator SwiftUIaccessibilityLinkedGroup1. - W przypadku treści podzielonej na strony cecha
causesPageTurnw połączeniu zaccessibilityScrollsprawia, że Speak Screen i VoiceOver automatycznie przewijają strony podczas odczytu całości1. - Tekst rysowany samodzielnie (zeskanowane strony, zaawansowana typografia) traci to wszystko. Pełne przyjęcie
UITextInputprzywraca to: geometrię przezselectionRects, podciągi przeztextInRangeoraz tokenizer do nawigacji po wierszu, słowie i znaku1. - Kontrolki niestandardowe kierują się czterema zasadami przewodnimi: cel, wartość, działania, informacja zwrotna. Narzędziami są
accessibilityLabel/accessibilityValue, cecha.adjustablewraz zaccessibilityAdjustableAction, akcje niestandardowe dla kontrolek wieloosiowych oraz bezpośredni dotyk (allowsDirectInteraction) dla powierzchni nasyconych gestami2.
Aplikacje do czytania: łączenie tekstu, który rozdzielił układ
Sesja o aplikacjach do czytania zbudowana jest wokół zwodniczo prostego ograniczenia. Aplikacja-przewodnik turystyczny prelegenta używa osobnego UITextView dla każdego akapitu, ponieważ wymagał tego układ, zamiast jednego widoku obejmującego całą stronę1. Każdy pojedynczy widok tekstu jest dostępny sam w sobie. Problem pojawia się na granicy między nimi.
Prelegent stawia aplikacji trzy cele: ziarnistą nawigację po tekście, aby VoiceOver i Speak Screen płynnie przemieszczały się przez tekst, ciągłe czytanie bez przerw oraz pełne zaznaczanie tekstu1. Reszta sesji to przegląd tego, które API spełnia każdy z nich.
W przypadku nawigacji między osobnymi widokami odpowiedzią są API elementów nawigacji po tekście wprowadzone w iOS 18. Dla każdego elementu tekstowego zwraca się następny i poprzedni dostępny element tekstowy, do którego VoiceOver ma się przemieścić. W przykładzie z sesji akapit 1 zwraca akapit 2 ze swojego accessibilityNextTextNavigationElement, a akapit 2 zwraca akapit 1 ze swojego accessibilityPreviousTextNavigationElement1. Po podłączeniu VoiceOver przechodzi poza koniec jednego akapitu na pierwszy wiersz następnego, zamiast odtwarzać dźwięk ślepego zaułka.
Dodatek z iOS 27 mieszka w SwiftUI. Jak ujmuje to prelegent, począwszy od iOS 27 połączenie wielu elementów tekstowych za pomocą modyfikatora accessibilityLinkedGroup daje ten sam efekt1. Połączonym elementom nadaje się ten sam id i namespace, a one dziedziczą zachowanie nawigacji po tekście między elementami bez ręcznego prowadzenia następnego i poprzedniego. AppKit otrzymuje odpowiednik accessibilitySharedTextUIElements dla tego samego rezultatu na Macu1. Artykuł Z czego zbudowany jest SwiftUI z tego klastra omawia, jak modyfikatory SwiftUI takie jak ten rozkładają się aż do leżącego u podstaw drzewa dostępności.
Ciągłość to drugi cel. Treść podzielona na strony wymaga przesuwania, a odczyt całości powinien ignorować granice stron tak, jak robi to audiobook. W sesji Speak Screen zatrzymuje się jak wryty na dole pierwszej strony, dopóki prelegent nie zastosuje cechy causesPageTurn do ostatniego akapitu na każdej stronie. W połączeniu z accessibilityScroll Speak Screen i VoiceOver przewijają się wtedy automatycznie do następnej strony po dotarciu do końca, a cecha jest dostępna zarówno w UIKit, jak i SwiftUI1.
Trzeci cel, zaznaczanie, w przeważającej części przychodzi za darmo z systemowymi widokami tekstu, ale sesja dodaje przemyślany akcent: akcję „Zapisz rekomendację” udostępnioną przez rotor edycji VoiceOver. Prelegent nadpisuje accessibilityCustomActions w widoku tekstu akapitu i buduje akcję niestandardową z kategorią edycji właśnie po to, aby pojawiła się w rotorze edycji obok operacji zaznaczania tekstu, a nie jako akcja ogólna1. Wskazówka jest jednoznaczna: kategorii edycji należy używać, gdy akcja niestandardowa wiąże się z zaznaczaniem tekstu.
Gdy sam rysujesz tekst: UITextInput w pełni
Druga połowa sesji o czytaniu zajmuje się przypadkiem, w którym widoki systemowe nie wchodzą w grę. Tekst niestandardowy pojawia się w dedykowanych aplikacjach do czytania o zaawansowanej typografii, we współdzielonym między aplikacjami kodzie lub na zeskanowanych stronach, a przykład prelegenta jest najostrzejszy: zastąpienie widoków tekstu z przewodnika turystycznego zeskanowanymi stronami z odręcznego notesu. Koszt jest całkowity. Przejście na obrazy traci zachowania dostępności, które UITextView dostarczał za darmo, aż po rzecz najbardziej podstawową: odczytanie tekstu na głos. VoiceOver mówi po prostu „Obraz”1.
Rozwiązaniem jest przyjęcie protokołu UITextInput, który może osadzić się na dowolnym elemencie dostępności i sprawić, że rysowany tekst lub tekst na obrazach będzie tak samo dostępny jak standardowy widok tekstu1. Haczyk, wyrażony w sesji wprost, polega na tym, że trzeba go zaimplementować w całości, aby uzyskać pełną korzyść. Prelegent omawia po kolei elementy nośne:
- Geometria.
selectionRectsoblicza prostokąty podświetlenia dla danego zakresu. Pracując z obrazem pisma odręcznego, prelegent wykorzystuje znaną wysokość i szerokość każdego wiersza, aby przybliżyć prostokąty za pomocą niestandardowej funkcjiselectionRectFromImage, a następnie zwraca złożoną tablicę1. - Podciągi.
textInRangezwraca tylko tę część tekstu, o którą pyta technologia wspomagająca1. - Tokenizer. Nawigacja po wierszu, zdaniu, słowie lub znaku przebiega przez tokenizer. Sesja tworzy podklasę
UITextInputStringTokenizerz UIKit, aby dopasować ją do niestandardowego układu1.
Jedno udoskonalenie jest wyraźnie opcjonalne. Aby zaznaczanie sprawiało wrażenie pełnego, z uchwytami i podświetleniami, prelegent dodaje UITextInteraction do widoku strony i wywołuje delegata wejścia, gdy zmienia się zaznaczenie, tak by system aktualizował warstwę wizualną. Sesja zaznacza, że tego kroku nie wymaga sam UITextInput; dopełnia on doświadczenie, by dorównało standardowemu widokowi tekstu1. A UITextInput komponuje się z wcześniejszymi API, więc causesPageTurn i elementy nawigacji działają również na tekście niestandardowym.
Jest tu nagroda, którą sesja wskazuje, a którą łatwo niedocenić: ta praca nie służy wyłącznie VoiceOverowi i Speak Screen. Od iOS 26 Accessibility Reader potrafi otworzyć treść aplikacji w widoku dostrojonym do łatwiejszego czytania, a te same praktyki dostępnego tekstu poprawiają również to doświadczenie1.
Kontrolki niestandardowe: cel, wartość, działania, informacja zwrotna
Sesja o kontrolkach niestandardowych otwiera się standardowym suwakiem SwiftUI i argumentem o tym, dlaczego on działa. Jednym spojrzeniem odczytuje się tor, uchwyt umieszczony w połowie, sugestię przeciągania oraz natychmiastową informację zwrotną. Nikt niczego nie wyjaśniał. Następnie sesja stawia oczywiste pytanie: a co, jeśli ktoś nie widzi ekranu? VoiceOver odpowiada, odczytując „Jasność, 50%, regulowane” wraz ze wskazówką, by przesunąć w górę lub w dół, co przekazuje te same cztery rzeczy co obraz: cel, wartość, dostępne działanie oraz informację zwrotną w miarę zmiany wartości2.
.adjustable z akcją regulującą.
Te cztery słowa (cel, wartość, działania, informacja zwrotna) to zasady przewodnie sesji, a każdy przykład do nich nawiązuje2. Pierwszym jest kontrolka dozownika kawy: przeciągnij w górę, by nalać więcej kawy, w dół, by mniej, a poziom napełnienia reprezentuje uncje. Przed jakąkolwiek pracą VoiceOver odczytuje ją jako ogólny „Przycisk, 6 uncji”, bez wskazówki, jak zmienić wartość2. Poprawki są przyrostowe:
- Cel i wartość.
accessibilityLabelnadaje jej nazwę „Dozownik kawy”;accessibilityValueogłasza bieżące napełnienie2. - Działanie. Cecha
.adjustablemówi VoiceOverowi, że kontrolka reaguje na przesunięcia w górę i w dół, aaccessibilityAdjustableActiondostarcza domknięcie z parametrem kierunku.incrementlub.decrement, by obsłużyć każdy przypadek2.
To daje regulację po jednej uncji naraz. Dla precyzyjniejszej kontroli sesja sięga po wbudowany w VoiceOver gest przekazania: podwójne stuknięcie z przytrzymaniem, które zaczyna się w accessibilityActivationPoint kontrolki i wysyła zdarzenia dotyku bezpośrednio do kontrolki w miarę ruchu palca. Prelegent ustawia punkt aktywacji tak, by odpowiadał bieżącemu poziomowi napełnienia2. Informacja zwrotna podczas przekazania to mała lekcja powściągliwości: sesja publikuje komunikat tylko wtedy, gdy wartość faktycznie się zmieniła i upłynęło co najmniej 0,3 sekundy, ponieważ ogłaszanie każdej zmiany byłoby hałaśliwe2.
Pad korektora podnosi poprzeczkę. To kontrolka dwuwymiarowa, a sesja szczerze przyznaje, że .adjustable to niewłaściwe narzędzie, ponieważ jego akcje zwiększania i zmniejszania obejmują jedną oś. Odpowiedzią są akcje niestandardowe: modyfikator accessibilityAction zastosowany czterokrotnie dla „przesuń w górę”, „przesuń w prawo”, „przesuń w dół” i „przesuń w lewo”, z których każda przesuwa jedną oś o stały krok ograniczony do zakresu2. W odróżnieniu od akcji regulującej, akcje niestandardowe obsługują dowolną zdefiniowaną przez ciebie operację i docierają również do osób korzystających ze Switch Control i Voice Control2.
Bezpośredni dotyk: gdy gesty są sednem
Ostatnim przykładem sesji jest kontrolka wirtualnego kota, którego się głaszcze, stuka i ściska, by uzyskać różne reakcje. Przekazanie pasuje tu słabo, zauważa prelegent, ponieważ ludzie mogą chcieć powtarzać działanie w kółko lub używać wielu gestów2. Dlatego kontrolka sięga po bezpośredni dotyk.
.accessibilityDirectTouch z .requiresActivation, tak by dotknięcia trafiały prosto do kota, zamiast być przechwytywane przez VoiceOver.
Cecha allowsDirectInteraction oznacza obszar jako strefę bezpośredniego dotyku: zdarzenia dotyku trafiają prosto do kontrolki, zamiast być przetwarzane przez VoiceOver, dzięki czemu działa każdy gest, który kontrolka obsługuje2. Dwie opcje kształtują to zachowanie. .requiresActivation utrzymuje kontrolkę w bezczynności aż do podwójnego stuknięcia, co pozwala przeciągać po ekranie bez przypadkowego jej uruchomienia, a bezpośredni dotyk pozostaje następnie aktywny, dopóki fokus nie opuści elementu. .silentOnTouch utrzymuje VoiceOver w ciszy nad obszarem, z myślą o kontrolkach, które wytwarzają własny dźwięk, który mowa VoiceOver w przeciwnym razie by zagłuszała2. Wirtualny kot używa .accessibilityDirectTouch z .requiresActivation2.
Sesja kończy się zastrzeżeniem, które nawiązuje do argumentu o dostępności jako platformie, rozwiniętego w Dostępność jako platforma: nie każdy potrafi wykonać gesty bezpośredniego dotyku, więc wszędzie, gdzie to możliwe, należy udostępnić inną drogę, na przykład akcje niestandardowe, aby osoby korzystające ze Switch Control i Voice Control dotarły do tych samych interakcji2.
Wskazówki dotyczące wdrożenia
Obie sesje kończą się tym samym poleceniem: włącz VoiceOver i sprawdź własną aplikację. Konkretnie:
- W przypadku powierzchni do czytania na systemowych widokach tekstu wypróbuj gest odczytu całości, nawiguj rotorem wierszy i zaznacz tekst. Jeśli odczyt całości zatrzymuje się na granicy strony, zastosuj
causesPageTurnrazem zaccessibilityScroll. Jeśli nawigacja po wierszach trafia w ślepy zaułek między osobnymi elementami tekstowymi, połącz je za pomocą API elementów nawigacji (UIKit) lubaccessibilityLinkedGroup(SwiftUI, iOS 27)1. - Jeśli sam rysujesz tekst, zaplanuj pełne, a nie częściowe przyjęcie
UITextInput; protokół działa na zasadzie wszystko albo nic, a opcjonalny krokUITextInteractionto właśnie to, co sprawia, że zaznaczanie sprawia wrażenie natywnego1. - W przypadku dowolnej kontrolki niestandardowej przejdź cztery zasady po kolei. Czy osoba korzystająca z VoiceOver może rozpoznać, czym to jest (etykieta), w jakim jest stanie (wartość), co może zrobić (cecha adjustable lub akcje niestandardowe) i co się stało (komunikaty)? Bezpośredni dotyk zarezerwuj dla kontrolek, których wartością jest sam gest, i połącz go z niegesturalną alternatywą2.
Powracający motyw: najpierw sięgaj po komponenty systemowe, a ścieżkę niestandardową traktuj jako wyjątek, który wymaga prawdziwej, kompletnej pracy. Artykuł Trzy powierzchnie aplikacji iOS ujmuje dostępność jako powierzchnię pierwszej klasy, na równi z widocznym UI i App Intents; te sesje pokazują, jak wygląda właściwe dopracowanie tej powierzchni w przypadku tekstu i kontrolek.
FAQ
Czym jest nowe API dostępności w iOS 27 dla aplikacji do czytania?
Modyfikatorem SwiftUI accessibilityLinkedGroup. Począwszy od iOS 27 połączenie wielu elementów tekstowych z tym samym id i namespace nadaje im nawigację po tekście między elementami, dzięki czemu VoiceOver przechodzi z ostatniego wiersza jednego elementu na pierwszy wiersz następnego. Jest to odpowiednik w SwiftUI API z iOS 18 accessibilityNextTextNavigationElement/accessibilityPreviousTextNavigationElement oraz accessibilitySharedTextUIElements z AppKit1.
Czy muszę implementować UITextInput, jeśli używam standardowego widoku tekstu?
Nie. UITextView (UIKit), TextEditor oraz Text z włączonym zaznaczaniem (SwiftUI), a także NSTextView (AppKit) już przyjmują UITextInput i od razu zapewniają nawigację po wierszu, słowie i znaku oraz zaznaczanie. UITextInput przyjmujesz samodzielnie tylko wtedy, gdy rysujesz tekst niestandardowy, na przykład zeskanowane strony lub zaawansowaną typografię, gdzie te zachowania systemowe są tracone1.
Kiedy kontrolka niestandardowa powinna używać cechy adjustable, a kiedy akcji niestandardowych?
Cechy .adjustable z accessibilityAdjustableAction używaj dla wartości jednoosiowych, gdzie zwiększanie i zmniejszanie ma sens, jak w suwaku. Akcji niestandardowych (modyfikator accessibilityAction) używaj, gdy jedna oś nie wystarcza, jak w padzie dwuwymiarowym, lub gdy chcesz udostępnić odrębne operacje, które VoiceOver odczytuje z nazwy. Pad korektora z sesji używa czterech akcji niestandardowych (przesuń w górę/w prawo/w dół/w lewo) właśnie dlatego, że cecha adjustable obejmuje tylko jeden kierunek2.
Czym jest bezpośredni dotyk i kiedy go używać?
Bezpośredni dotyk (cecha allowsDirectInteraction, stosowana przez .accessibilityDirectTouch w SwiftUI) oznacza obszar tak, by dotknięcia trafiały prosto do twojej kontrolki, zamiast być przetwarzane przez VoiceOver, co pozwala używać każdego gestu, który kontrolka obsługuje. Używaj go dla kontrolek nasyconych gestami, gdzie gest przekazania pasuje słabo, i połącz go z .requiresActivation, aby zapobiec przypadkowym uruchomieniom. Zawsze oferuj niegesturalną alternatywę, na przykład akcje niestandardowe, dla osób, które nie potrafią wykonać gestów bezpośredniego dotyku2.
Jak dostępny tekst pomaga funkcjom wykraczającym poza VoiceOver?
Ta sama praca procentuje w Speak Screen oraz, od iOS 26, w Accessibility Reader, który otwiera treść aplikacji w widoku dostrojonym do łatwiejszego czytania. Wdrożenie praktyk nawigacji po tekście, przewracania stron i UITextInput omówionych na sesji o czytaniu poprawia wszystkie trzy doświadczenia z jednego zestawu zmian1.
Powiązane materiały
- Dostępność jako platforma: Personal Voice, Live Speech, śledzenie wzroku, Music Haptics
- Z czego zbudowany jest SwiftUI
- Trzy powierzchnie aplikacji iOS
- Powierzchnia widżetów i kontrolek w iOS 26
- Hub: Seria Apple Ecosystem
- Przewodnik: Tworzenie agentów iOS
Bibliografia
-
Apple, sesja WWDC26 219, „Enhance the accessibility of your reading app”. developer.apple.com/videos/play/wwdc2026/219. Źródło dla systemowych widoków tekstu (
UITextView,TextEditor,Textz zaznaczaniem,NSTextView) przyjmującychUITextInput; API z iOS 18accessibilityNextTextNavigationElement/accessibilityPreviousTextNavigationElementoraz modyfikatora SwiftUI z iOS 27accessibilityLinkedGroup(AppKit:accessibilitySharedTextUIElements);causesPageTurnzaccessibilityScroll; akcji zaznaczania tekstu przezaccessibilityCustomActionsz kategorią edycji; pełnego przyjęciaUITextInputdla tekstu niestandardowego (selectionRects,textInRange,UITextInputStringTokenizer) oraz opcjonalnejUITextInteraction; a także Accessibility Reader z iOS 26. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple, sesja WWDC26 220, „Refine accessibility for custom controls”. developer.apple.com/videos/play/wwdc2026/220. Źródło dla zasad cel/wartość/działania/informacja zwrotna; kontrolki dozownika kawy używającej
accessibilityLabel,accessibilityValue, cechy.adjustableorazaccessibilityAdjustableAction; gestu przekazania waccessibilityActivationPointz ograniczanymi komunikatami (zmiana wartości oraz upłynięcie 0,3 sekundy); modyfikatoraaccessibilityActiondla padu korektora; oraz bezpośredniego dotyku przezallowsDirectInteraction(.accessibilityDirectTouch) z.requiresActivationi.silentOnTouchdla kontrolki wirtualnego kota, a także przypomnienia o niegesturalnej alternatywie. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩