iOS 27のアクセシビリティ:リーディングアプリとカスタムコントロール
長文コンテンツを読むことは、UIを操作することとは別種の課題です。目的はコントロール間を飛び移ることではなく、テキストの中をなめらかに移動することにあります。WWDC26の2つのアクセシビリティセッションは、まさにこの境目に沿って分かれています。一方はリーディングという面を、もう一方はそれを取り囲むコントロールを扱っているのです。
この分け方が重要なのは、修正の性質そのものが異なるからです。リーディングアプリの不具合は連続性に関わります。段落をまたいでつながらないテキスト、ページの末尾で止まってしまう読み上げといったものです。一方、カスタムコントロールの不具合は変換に関わります。すべてを視覚的に伝えていながら、VoiceOverには何も伝えていないジェスチャーがその例です。iOS 27はその両方を狙ったAPIを提供しており、そのうちの1つ、accessibilityLinkedGroupは今年の新機能となります。
TL;DR
- リーディングアプリは、まずシステムのテキストビューを選ぶべきです。
UITextView、TextEditor、そして選択を有効にしたTextはUITextInputプロトコルを採用しており、行・単語・文字単位のナビゲーションと選択を無償で得られます1。 - レイアウトの都合でテキスト要素を分割せざるを得ない場合は、それらをリンクしてVoiceOverが境目を越えて移動できるようにします。iOS 18では
accessibilityNextTextNavigationElement/accessibilityPreviousTextNavigationElementが導入され、iOS 27では同じ効果を持つSwiftUIのaccessibilityLinkedGroupモディファイアが加わります1。 - ページ分割されたコンテンツでは、
causesPageTurnトレイトとaccessibilityScrollを組み合わせることで、読み上げの最中にSpeak ScreenとVoiceOverが自動的にページを進めるようになります1。 - 独自に描画したテキスト(スキャンしたページや高度なタイポグラフィなど)では、これらすべてが失われます。
UITextInputを完全に採用すれば復元できます。selectionRectsによるジオメトリ、textInRangeによる部分文字列、そして行・単語・文字ナビゲーションのためのトークナイザーです1。 - カスタムコントロールは4つの指針に従います。目的・値・操作・フィードバックです。そのための手段は、
accessibilityLabel/accessibilityValue、accessibilityAdjustableActionを伴う.adjustableトレイト、多軸コントロール向けのカスタムアクション、そしてジェスチャー中心の面に向けたダイレクトタッチ(allowsDirectInteraction)です2。
リーディングアプリ:レイアウトが引き離したテキストをつなぐ
リーディングアプリのセッションは、一見すると単純な制約を軸に組み立てられています。発表者の旅行ガイドアプリは、ページ全体を1つのビューに収めるのではなく、レイアウト上の要請から段落ごとに別々のUITextViewを使っています1。個々のテキストビューはそれ自体としてはアクセシブルです。問題はそれらの境界に現れます。
発表者はアプリに3つの目標を掲げます。VoiceOverとSpeak Screenがなめらかにテキストを移動できる細かなテキストナビゲーション、中断のない連続した読書体験、そして完全なテキスト選択です1。セッションの残りは、それぞれをどのAPIが満たすかを巡る案内となっています。
別々のビューをまたぐナビゲーションについては、その答えはiOS 18で導入されたテキストナビゲーション要素のAPIです。各テキスト要素について、VoiceOverが次に移動すべき前後のアクセシブルなテキスト要素を返します。セッションの例では、段落1はaccessibilityNextTextNavigationElementから段落2を返し、段落2はaccessibilityPreviousTextNavigationElementから段落1を返します1。一度組み込めば、VoiceOverは行き止まりの音を鳴らす代わりに、1つの段落の末尾を越えて次の段落の最初の行へと移ります。
iOS 27での追加はSwiftUIにあります。発表者の言葉を借りれば、iOS 27以降はaccessibilityLinkedGroupモディファイアで複数のテキスト要素をリンクすることで同じ効果が得られます1。リンクする要素に同じidとnamespaceを与えると、手動で前後を管理することなく要素間のテキストナビゲーション動作を継承します。AppKitでは、Mac上で同じ結果を得るための対応物accessibilitySharedTextUIElementsが用意されています1。このクラスター内の記事SwiftUIを構成するものでは、このようなSwiftUIモディファイアが内部のアクセシビリティツリーへとどのように解決されるかを扱っています。
連続性が2つ目の目標です。ページ分割されたコンテンツにはスワイプが必要ですが、読み上げはオーディオブックのようにページの境目を無視すべきです。セッションでは、発表者が各ページの最後の段落にcausesPageTurnトレイトを適用するまで、Speak Screenは最初のページの末尾でぴたりと止まってしまいます。accessibilityScrollと組み合わせると、末尾に達したときにSpeak ScreenとVoiceOverが自動的に次のページへスクロールするようになります。このトレイトはUIKitとSwiftUIの両方で利用できます1。
3つ目の目標である選択は、その大部分がシステムのテキストビューから無償で得られますが、セッションは行き届いた工夫を加えています。VoiceOverの編集ローターから呼び出せる「おすすめに保存」アクションです。発表者は段落のテキストビューでaccessibilityCustomActionsをオーバーライドし、編集カテゴリを指定してカスタムアクションを構築します。これは、汎用的なアクションとしてではなく、テキスト選択の操作と並んで編集ローターに表示させるためです1。ガイダンスは明確です。カスタムアクションがテキスト選択に関連する場合は、編集カテゴリを使いましょう。
自前でテキストを描画する場合:UITextInputを完全に
リーディングセッションの後半は、システムビューが選択肢にならない場合を扱います。独自テキストは、高度なタイポグラフィを備えた専用リーディングアプリ、アプリをまたいで共有されるコード、あるいはスキャンしたページに現れます。発表者の例はもっとも鋭いものです。旅行ガイドのテキストビューを、手書きノートをスキャンしたページに置き換えるのです。その代償は全面的です。画像に切り替えると、UITextViewが無償で提供していたアクセシビリティ動作が失われ、もっとも基本的なこと、つまりテキストの読み上げすらできなくなります。VoiceOverはただ「イメージ」と言うだけになるのです1。
その修正はUITextInputプロトコルを採用することです。これはどのアクセシビリティ要素にも載せることができ、描画されたテキストや画像内のテキストを標準のテキストビューと同じくらいアクセシブルにします1。ただし、セッションで率直に語られている注意点があります。完全な恩恵を得るには、これを全面的に実装しなければならないということです。発表者は要となる部分を順に説明していきます。
- ジオメトリ。
selectionRectsは、指定した範囲に対するハイライトの矩形を計算します。手書き画像を扱う発表者は、各行の既知の高さと幅を使い、カスタムのselectionRectFromImage関数で矩形を近似し、組み立てた配列を返します1。 - 部分文字列。
textInRangeは、支援技術が問い合わせたテキストの一部だけを返します1。 - トークナイザー。 行・文・単語・文字によるナビゲーションはトークナイザーを通じて行われます。セッションでは、UIKitの
UITextInputStringTokenizerをサブクラス化してカスタムレイアウトに合わせています1。
ある仕上げは明確に任意とされています。ハンドルとハイライトで選択を完成された体験にするため、発表者はページビューにUITextInteractionを追加し、選択が変わったときに入力デリゲートを呼び出してシステムに表示を更新させます。セッションは、この手順はUITextInput自体が要求するものではないと述べています。標準のテキストビューに匹敵するよう体験を仕上げるためのものなのです1。そしてUITextInputは先のAPIと組み合わさるため、causesPageTurnやナビゲーション要素はカスタムテキストでも機能します。
セッションが指摘する、見落としがちな報いがあります。この作業はVoiceOverとSpeak Screenだけに役立つのではないのです。iOS 26以降、Accessibility Readerはより読みやすく調整された表示でアプリのコンテンツを開くことができ、同じアクセシブルなテキストの実践はその体験も向上させます1。
カスタムコントロール:目的・値・操作・フィードバック
カスタムコントロールのセッションは、標準的なSwiftUIのスライダーと、それがなぜ機能するのかという論から始まります。トラック、中央に配置されたハンドル、ドラッグできることを示す手がかり、そして即座のフィードバックを、ひと目で読み取れます。誰も何も説明していません。そこでセッションは当然の問いを投げかけます。画面が見えない人がいたらどうなるのか、と。VoiceOverは「明るさ、50%、調整可能」と読み上げ、上下にスワイプするヒントを添えることで答えを返します。これは視覚的に伝わる同じ4つのこと、すなわち目的、値、利用可能な操作、そして値が変わったときのフィードバックを伝えているのです2。
.adjustableトレイトを加えることで、VoiceOverで操作できる調整可能なスライダーへと変えます。
この4つの言葉(目的・値・操作・フィードバック)がセッションの指針であり、あらゆる例がそこへ立ち返ります2。最初の例はコーヒーディスペンサーのコントロールです。上にドラッグすればコーヒーが増え、下にドラッグすれば減り、満たされた高さがオンスを表します。何も手を加えないうちは、VoiceOverはこれを汎用の「ボタン、6オンス」として読み上げ、値の変え方をまったく伝えません2。修正は段階的です。
- 目的と値。
accessibilityLabelがこれを「コーヒーディスペンサー」と名付け、accessibilityValueが現在の量を知らせます2。 - 操作。
.adjustableトレイトは、このコントロールが上下のスワイプに反応することをVoiceOverに伝え、accessibilityAdjustableActionは.incrementまたは.decrementという方向パラメータを持つクロージャを提供して、それぞれの場合を処理します2。
これで1オンスずつの調整ができるようになります。より細かな制御のために、セッションはVoiceOverに組み込まれたパススルージェスチャーを用います。これはダブルタップして長押しするもので、コントロールのaccessibilityActivationPointから始まり、指が動くにつれてタッチイベントを直接コントロールへ送ります。発表者はアクティベーションポイントを現在の量に合わせて設定します2。パススルー中のフィードバックは、抑制についての小さな学びになっています。セッションは、値が実際に変化し、なおかつ0.3秒以上が経過したときにのみアナウンスを発します。すべての変化をアナウンスしてはうるさくなってしまうからです2。
イコライザーパッドは難度を一段引き上げます。これは2次元のコントロールであり、セッションは.adjustableが不適切な手段であることをはっきり認めます。その増減アクションは1つの軸しかカバーしないからです。答えはカスタムアクションです。accessibilityActionモディファイアを「上に移動」「右に移動」「下に移動」「左に移動」の4回適用し、それぞれが1つの軸を範囲内に収まるよう固定のステップだけ動かします2。調整アクションとは異なり、カスタムアクションは定義した任意の操作をサポートし、Switch ControlやVoice Controlのユーザーにも届きます2。
ダイレクトタッチ:ジェスチャーこそが要となるとき
セッションの最後の例は、なでたり、タップしたり、ピンチしたりすると異なる反応を返す仮想の猫のコントロールです。ここではパススルーは適していないと発表者は述べます。同じ操作を何度も繰り返したい人や、複数のジェスチャーを使いたい人がいるかもしれないからです2。そこでこのコントロールはダイレクトタッチを用います。
.requiresActivation付きの.accessibilityDirectTouchモディファイアを加え、タッチがVoiceOverに横取りされずにそのまま猫へ届くようにします。
allowsDirectInteractionトレイトは、領域をダイレクトタッチエリアとして指定します。タッチイベントはVoiceOverに処理されずそのままコントロールへ渡るため、コントロールが対応するあらゆるジェスチャーが機能します2。2つのオプションがその挙動を形づくります。.requiresActivationは、ダブルタップされるまでコントロールを反応しない状態に保ち、誤って起動することなく画面上をドラッグできるようにします。そしてダイレクトタッチは、フォーカスがその要素から離れるまで有効なままになります。.silentOnTouchは、その領域でVoiceOverを黙らせます。これは、VoiceOverの読み上げが重なってしまうような独自の音声を発するコントロールを想定したものです2。仮想の猫は.requiresActivation付きの.accessibilityDirectTouchを使います2。
セッションは、プラットフォームとしてのアクセシビリティで展開されたプラットフォームアクセシビリティの論につながる注意点で締めくくられます。すべての人がダイレクトタッチのジェスチャーを行えるわけではないため、可能なかぎりカスタムアクションのような別の経路を用意し、Switch ControlやVoice Controlのユーザーが同じ操作にたどり着けるようにしましょう2。
採用のためのガイダンス
どちらのセッションも同じ指示で終わります。VoiceOverをオンにして、自分のアプリを自ら点検することです。具体的には、
- システムのテキストビュー上のリーディング面では、読み上げジェスチャーを試し、行ローターでナビゲートし、テキストを選択してみましょう。読み上げがページの境目で止まるなら、
accessibilityScrollとともにcausesPageTurnを採用します。別々のテキスト要素の間で行ナビゲーションが行き止まりになるなら、ナビゲーション要素のAPI(UIKit)かaccessibilityLinkedGroup(SwiftUI、iOS 27)でそれらをリンクします1。 - 自前でテキストを描画する場合は、部分的ではなく完全な
UITextInputの採用を計画しましょう。このプロトコルはオール・オア・ナッシングであり、任意のUITextInteractionの手順こそが選択をネイティブに感じさせるものです1。 - どんなカスタムコントロールでも、4つの指針を順にたどりましょう。VoiceOverのユーザーが、それが何か(ラベル)、どんな状態か(値)、何ができるか(adjustableトレイトまたはカスタムアクション)、そして何が起きたか(アナウンス)を把握できるでしょうか。ダイレクトタッチは、その値がジェスチャーそのものであるコントロールに限って使い、ジェスチャー以外のフォールバックと組み合わせます2。
繰り返し現れるテーマは、まずシステムコンポーネントを選ぶこと、そしてカスタムの道は、本物の完全な作業を要する例外として扱うことです。iOSアプリの3つの面の記事は、アクセシビリティを、目に見えるUIやApp Intentsと並ぶ一級の面として位置づけています。これらのセッションは、その面をテキストとコントロールについて正しく仕上げるとはどういうことかを示しているのです。
FAQ
iOS 27のリーディングアプリ向けの新しいアクセシビリティAPIとは何ですか?
SwiftUIのaccessibilityLinkedGroupモディファイアです。iOS 27以降、同じidとnamespaceで複数のテキスト要素をリンクすると、要素間のテキストナビゲーションが得られ、VoiceOverがある要素の最後の行から次の要素の最初の行へと移動します。これはiOS 18のaccessibilityNextTextNavigationElement/accessibilityPreviousTextNavigationElement API、およびAppKitのaccessibilitySharedTextUIElementsに相当するSwiftUI版です1。
標準のテキストビューを使う場合、UITextInputを実装する必要はありますか?
いいえ。UITextView(UIKit)、TextEditor、選択を有効にしたText(SwiftUI)、そしてNSTextView(AppKit)は、すでにUITextInputを採用しており、行・単語・文字ナビゲーションと選択を最初から提供しています。UITextInputを自分で採用するのは、スキャンしたページや高度なタイポグラフィのように独自テキストを描画し、それらのシステム動作が失われる場合だけです1。
カスタムコントロールでは、adjustableトレイトとカスタムアクションをどう使い分けますか?
スライダーのように、増加と減少が意味をなす単一軸の値には、accessibilityAdjustableActionを伴う.adjustableトレイトを使います。2次元のパッドのように1つの軸では足りない場合や、VoiceOverが名前で読み上げる個別の操作を提示したい場合には、カスタムアクション(accessibilityActionモディファイア)を使います。セッションのイコライザーパッドが4つのカスタムアクション(上・右・下・左へ移動)を使うのは、まさにadjustableトレイトが1方向しかカバーしないからです2。
ダイレクトタッチとは何で、いつ使うべきですか?
ダイレクトタッチ(allowsDirectInteractionトレイト。SwiftUIでは.accessibilityDirectTouchで適用)は、タッチがVoiceOverに処理されずそのままコントロールへ渡るよう領域を指定し、コントロールが対応するあらゆるジェスチャーを使えるようにします。パススルージェスチャーが適さないジェスチャー中心のコントロールに使い、誤起動を防ぐために.requiresActivationと組み合わせます。ダイレクトタッチのジェスチャーを行えない人のために、カスタムアクションのようなジェスチャー以外のフォールバックを常に用意しましょう2。
アクセシブルなテキストは、VoiceOver以外の機能にどう役立ちますか?
同じ作業はSpeak Screenで、そしてiOS 26以降はAccessibility Readerで報われます。Accessibility Readerは、より読みやすく調整された表示でアプリのコンテンツを開きます。リーディングセッションが扱うテキストナビゲーション、ページめくり、UITextInputの実践を取り入れれば、1組の変更で3つの体験すべてが向上します1。
関連記事
- プラットフォームとしてのアクセシビリティ:Personal Voice、Live Speech、視線追跡、Music Haptics
- SwiftUIを構成するもの
- iOSアプリの3つの面
- iOS 26のウィジェットとコントロールの面
- ハブ:Apple Ecosystemシリーズ
- ガイド:iOSエージェント開発
参考文献
-
Apple、WWDC26セッション219「Enhance the accessibility of your reading app」。developer.apple.com/videos/play/wwdc2026/219。
UITextInputを採用するシステムのテキストビュー(UITextView、TextEditor、選択を有効にしたText、NSTextView)、iOS 18のaccessibilityNextTextNavigationElement/accessibilityPreviousTextNavigationElementAPIとiOS 27のSwiftUIaccessibilityLinkedGroupモディファイア(AppKit:accessibilitySharedTextUIElements)、accessibilityScrollを伴うcausesPageTurn、編集カテゴリを用いたaccessibilityCustomActionsによるテキスト選択アクション、カスタムテキスト向けの完全なUITextInput採用(selectionRects、textInRange、UITextInputStringTokenizer)と任意のUITextInteraction、そしてiOS 26のAccessibility Readerに関する出典。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple、WWDC26セッション220「Refine accessibility for custom controls」。developer.apple.com/videos/play/wwdc2026/220。目的・値・操作・フィードバックの指針、
accessibilityLabel・accessibilityValue・.adjustableトレイト・accessibilityAdjustableActionを用いたコーヒーディスペンサーのコントロール、スロットル付きアナウンス(値の変化に加えて0.3秒の経過)を伴うaccessibilityActivationPointでのパススルージェスチャー、イコライザーパッド向けのaccessibilityActionモディファイア、そして仮想の猫のコントロール向けの.requiresActivationと.silentOnTouchを伴うallowsDirectInteraction(.accessibilityDirectTouch)によるダイレクトタッチ、加えてジェスチャー以外のフォールバックの注意喚起に関する出典。 ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩