← Wszystkie wpisy

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, TextEditor oraz Text z włączonym zaznaczaniem przyjmują protokół UITextInput i 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 SwiftUI accessibilityLinkedGroup1.
  • W przypadku treści podzielonej na strony cecha causesPageTurn w połączeniu z accessibilityScroll sprawia, ż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 UITextInput przywraca to: geometrię przez selectionRects, podciągi przez textInRange oraz 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 .adjustable wraz z accessibilityAdjustableAction, 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.

Watch on Apple Developer ↗
Apple pokazuje VoiceOver utknięty na nawigacji wiersz po wierszu wewnątrz jednego akapitu, niezdolny przejść do następnego, ponieważ każdy akapit jest osobnym widokiem, a następnie łączy je za pomocą API elementów nawigacji po tekście.

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. selectionRects oblicza 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 funkcji selectionRectFromImage, a następnie zwraca złożoną tablicę1.
  • Podciągi. textInRange zwraca 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ę UITextInputStringTokenizer z 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.

Watch on Apple Developer ↗
Apple zamienia niestandardową kontrolkę dozownika kawy z surowego „przycisk, 6 uncji” w regulowany suwak, którym VoiceOver może sterować, dodając etykietę, wartość oraz cechę .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:

  1. Cel i wartość. accessibilityLabel nadaje jej nazwę „Dozownik kawy”; accessibilityValue ogłasza bieżące napełnienie2.
  2. Działanie. Cecha .adjustable mówi VoiceOverowi, że kontrolka reaguje na przesunięcia w górę i w dół, a accessibilityAdjustableAction dostarcza domknięcie z parametrem kierunku .increment lub .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.

Watch on Apple Developer ↗
Apple dodaje do kontrolki sterowanej gestami modyfikator .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 causesPageTurn razem z accessibilityScroll. 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) lub accessibilityLinkedGroup (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 krok UITextInteraction to 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

Bibliografia


  1. 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, Text z zaznaczaniem, NSTextView) przyjmujących UITextInput; API z iOS 18 accessibilityNextTextNavigationElement/accessibilityPreviousTextNavigationElement oraz modyfikatora SwiftUI z iOS 27 accessibilityLinkedGroup (AppKit: accessibilitySharedTextUIElements); causesPageTurn z accessibilityScroll; akcji zaznaczania tekstu przez accessibilityCustomActions z kategorią edycji; pełnego przyjęcia UITextInput dla tekstu niestandardowego (selectionRects, textInRange, UITextInputStringTokenizer) oraz opcjonalnej UITextInteraction; a także Accessibility Reader z iOS 26. 

  2. 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 .adjustable oraz accessibilityAdjustableAction; gestu przekazania w accessibilityActivationPoint z ograniczanymi komunikatami (zmiana wartości oraz upłynięcie 0,3 sekundy); modyfikatora accessibilityAction dla padu korektora; oraz bezpośredniego dotyku przez allowsDirectInteraction (.accessibilityDirectTouch) z .requiresActivation i .silentOnTouch dla kontrolki wirtualnego kota, a także przypomnienia o niegesturalnej alternatywie. 

Powiązane artykuły

Wydajność i interoperacyjność SwiftUI w iOS 27

Jak SwiftUI w iOS 27 obsługuje przewijanie leniwych stosów, efekty shaderów GPU oraz interoperacyjność z AppKit/UIKit, n…

18 min czytania

Co nowego w SwiftUI w systemie iOS 27

iOS 27 przebudowuje listy, dokumenty, paski narzędzi i błędy w SwiftUI: zmiana kolejności przeciąganiem, model dokumentu…

20 min czytania

Stos agentowy inżyniera designu

Inżynierowie designu potrzebują infrastruktury agentowej wymuszającej spójność wizualną, dyscyplinę typograficzną, zgodn…

11 min czytania