ios-accessibility について
SwiftUI、UIKit、AppKit を使う iOS・macOS 開発者向けのアクセシビリティスキルです。VoiceOver、スイッチコントロール、音声コントロール、キーボードで操作できる画面の設計や、フォーカス、設定、テストを扱います。
主な機能
- 01支援技術向けの意味とラベル
- 02アクセシビリティフォーカス管理
- 03Dynamic Type とシステム設定
- 04VoiceOver ローターとカスタム操作
- 05UIKit・AppKit の対応パターン
- 06テストとレビュー項目
活用シーン
- 01VoiceOver での画面操作を確認
- 02モーダル終了後にフォーカスを戻す
- 03ジェスチャーに代わる操作手段を用意
- 04Dynamic Type と視差効果低減への対応を確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill ios-accessibilityインストール後、Claude Code で/ios-accessibilityを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
evals/evals.jsonreferences/a11y-patterns.mdmedia-accessibility.mdnutrition-labels.md詳細
iOS/macOS アクセシビリティ — SwiftUI、UIKit、AppKit
VoiceOver、Switch Control、Voice Control、フルキーボードアクセス、適応型アクセシビリティ設定でも操作できる SwiftUI、UIKit、AppKit のインターフェースを構築します。
目次
- 基本原則
- VoiceOver による要素の読み上げ方
- SwiftUI のアクセシビリティ修飾子
- フォーカス管理
- ダイナミックタイプ
- カスタムローター
- システムのアクセシビリティ設定
- 装飾コンテンツ
- Voice Control
- Switch Control
- フルキーボードアクセス
- アシスティブアクセス(iOS 18 以降)
- UIKit のアクセシビリティパターン
- AppKit のアクセシビリティパターン
- アクセシビリティのカスタムコンテンツ
- App Store のアクセシビリティ情報ラベル
- アクセシビリティのテスト
- よくある間違い
- 確認チェックリスト
- 参考資料
基本原則
- システムのセマンティックなコントロールを優先します。カスタムコントロールでは、同じ名前、 役割、値、状態、アクションを公開する必要があります。
- 支援入力でも、すべてのタスクを実行できるようにします。ジェスチャ、色、 動き、ホバー状態、視覚的な配置だけを、意味やアクションを伝える唯一の方法にしないでください。
- ナビゲーション、表示、更新、 閉じる操作を通じて、フォーカスと移動順序を意図的に保ちます。
- テキスト、レイアウト、コントラスト、透明度、動きが、ユーザーの アクセシビリティ設定に応じて変化するようにします。
- App Store のアクセシビリティに関する申告は、個々のコントロール単位のサポートをまとめたものではなく、 製品に関する根拠のある主張として扱います。
VoiceOver による要素の読み上げ方
VoiceOver は、要素のプロパティを固定された変更不可の順序で読み上げます。
ラベル -> 値 -> 特性 -> ヒント
この読み上げ順を考慮して、ラベル、値、ヒントを設計してください。
SwiftUI のアクセシビリティ修飾子
SwiftUI の修飾子の詳細な例(ラベル、ヒント、特性、グループ化、カスタムコントロール、調整可能なアクション、カスタムアクション)については、references/a11y-patterns.md を参照してください。
フォーカス管理
多くのアプリで問題が発生するのがフォーカス管理です。シート、アラート、ポップオーバーを閉じたとき、VoiceOver のフォーカス MUST は、それを起動した要素に戻る必要があります。
このセクションでは、支援技術のアクセシビリティフォーカスについて説明します。キーボードフォーカス、方向キーによるフォーカス、focusSection()、シーンにフォーカスする値、UIFocusGuide については、focus-engine スキルを使用してください。
移動順序で要素の順序とグループ化を監査し、キーボードフォーカスの仕組みについては focus-engine を参照してください。
@AccessibilityFocusState(iOS 15 以降)
@AccessibilityFocusState は、現在のアクセシビリティフォーカスを読み書きするプロパティラッパーです。単一の対象にフォーカスする場合は Bool、複数の対象にフォーカスする場合は任意の Hashable な列挙型を使用できます。
struct ContentView: View {
@State private var showSheet = false
@AccessibilityFocusState private var focusOnTrigger: Bool
var body: some View {
Button("Open Settings") { showSheet = true }
.accessibilityFocused($focusOnTrigger)
.sheet(isPresented: $showSheet) {
SettingsSheet()
.onDisappear {
// Slight delay allows the transition to complete before moving focus
Task { @MainActor in
try? await Task.sleep(for: .milliseconds(100))
focusOnTrigger = true
}
}
}
}
}
列挙型による複数対象へのフォーカス
enum A11yFocus: Hashable {
case nameField
case emailField
case submitButton
}
struct FormView: View {
@AccessibilityFocusState private var focus: A11yFocus?
var body: some View {
Form {
TextField("Name", text: $name)
.accessibilityFocused($focus, equals: .nameField)
TextField("Email", text: $email)
.accessibilityFocused($focus, equals: .emailField)
Button("Submit") { validate() }
.accessibilityFocused($focus, equals: .submitButton)
}
}
func validate() {
if name.isEmpty {
focus = .nameField // Move VoiceOver to the invalid field
}
}
}
カスタムモーダル
カスタムオーバーレイビューでは、VoiceOver のフォーカスを閉じ込める .isModal 特性と、閉じるためのエスケープアクションが必要です。
CustomDialog()
.accessibilityAddTraits(.isModal)
.accessibilityAction(.escape) { dismiss() }
モーダルの仕様の一部として、閉じる操作をテストしてください。ユーザーは、該当する支援技術のエスケープジェスチャまたはキーボードのエスケープ操作でオーバーレイを閉じられる必要があります。また、フォーカスは起動元または次の論理的な対象に戻る必要があります。
アクセシビリティ通知(UIKit)
UIKit のコンテキストで変更を読み上げたり、命令的にフォーカスを移動したりする必要がある場合は、次のようにします。
// Announce a status change (e.g., "Item deleted", "Upload complete")
UIAccessibility.post(notification: .announcement, argument: "Upload complete")
// Partial screen update -- move focus to a specific element
UIAccessibility.post(notification: .layoutChanged, argument: targetView)
// Full screen transition -- move focus to the new screen
UIAccessibility.post(notification: .screenChanged, argument: newScreenView)
Dynamic Type
システムのテキストスタイルに合わせてテキストを拡大縮小します。テキスト以外の寸法も拡大縮小します。アイコンサイズ、間隔、コントロールの高さ、カスタムのヒット領域の寸法など、テキストサイズに追従させる必要がある場合は、@ScaledMetric(relativeTo:) を使用してください。
@ScaledMetric や最小タップターゲットのパターンを含む、Dynamic Type とアダプティブレイアウトの例については、references/a11y-patterns.md を参照してください。
カスタムローター
ローターを使うと、VoiceOver のユーザーは特定の種類のコンテンツにすばやく移動できます。コンテンツの多い画面にはカスタムローターを追加してください。ローターの完全な例については、references/a11y-patterns.md を参照してください。
システムのアクセシビリティ設定
次の環境値を必ず尊重してください。
@Environment(\.accessibilityReduceMotion) var reduceMotion
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
@Environment(\.colorSchemeContrast) var contrast // .standard or .increased
@Environment(\.legibilityWeight) var legibilityWeight // .regular or .bold
視差効果を減らす
動きに基づくアニメーションは、クロスフェードまたはアニメーションなしに置き換えてください。
withAnimation(reduceMotion ? nil : .spring()) {
showContent.toggle()
}
content.transition(reduceMotion ? .opacity : .slide)
行の削除、数量の変更、シートやチェックアウトの表示、モーダルの閉じる動作など、動きを伴うすべてのトランジションを確認してください。視差効果を減らす設定が有効な場合は、スライド、バウンス、視差、スプリング、大きな空間的トランジションを、不透明度の変更、状態の即時変更、またはアニメーションなしに置き換えてください。
透明度を下げる、コントラストを上げる、テキストを太字にする
// Solid backgrounds when transparency is reduced
.background(reduceTransparency ? Color(.systemBackground) : Color(.systemBackground).opacity(0.85))
// Stronger colors when contrast is increased
.foregroundStyle(contrast == .increased ? .primary : .secondary)
// Bold weight when system bold text is enabled
.fontWeight(legibilityWeight == .bold ? .bold : .regular)
装飾的なコンテンツ
// Decorative images: hidden from VoiceOver
Image(decorative: "background-pattern")
Image("visual-divider").accessibilityHidden(true)
// Icon next to text: Label handles this automatically
Label("Settings", systemImage: "gear")
// Icon-only buttons: MUST have an accessibility label
Button(action: { }) {
Image(systemName: "gear")
}
.accessibilityLabel("Settings")
画像が隣接するアクセシブルなテキスト以上の情報を加えない場合に限り、装飾として扱ってください。商品のバリエーション、状態、グラフのデータ点、ユーザー生成コンテンツ、その他の識別に関わる詳細を伝える画像は、非表示にせず、意味のある説明を付けてください。
音声コントロール
音声コントロールはアクセシビリティラベルを使って、音声で読み上げるタップターゲットを生成します。ラベルがない場合や読み上げられない場合、音声コントロールでその要素を選択できません。
- すべてのインタラクティブ要素に、読み上げ可能なアクセシビリティラベルを必ず設定してください(絵文字のみ、記号のみは不可)。
- ラベルは表示中の画面内で一意にしてください。ラベルが重複すると、ユーザーはオーバーレイの番号を使って区別しなければなりません。
- 長くて扱いにくいラベル、ローカライズされたラベル、頭字語の多いラベル、一般に短縮されるラベルには、
accessibilityInputLabelsをリリース前のアクセシビリティ作業として扱ってください。仕上げの作業として後回しにしないでください。音声コントロールとフルキーボードアクセスでは、これらのラベルが使用されます。代替ラベルは重要度の高い順に並べてください。 - 繰り返し表示される行アクション、数量コントロール、アカウント/設定へのリンク、メディアコントロール、頭字語や製品名を含むローカライズされたラベルなど、主となるラベルを発音しにくい表示中のターゲットには、広く
accessibilityInputLabelsを適用してください。 - 音声コントロールを有効にしてテストしてください。「名前を表示」と「番号を表示」を読み上げ、すべてのインタラクティブ要素を選択できることを確認してください。
- 音声コントロールのレビューでは、両方のオーバーレイを確認してください。「名前を表示」では読み上げ可能なラベルを確認し、「番号を表示」では、名前がない、重複している、または発音しにくい場合でも、表示中のすべてのインタラクティブターゲットに到達できることを確認します。
accessibilityInputLabels の例と、読み上げ可能なラベルのガイドラインについては、references/a11y-patterns.md を参照してください。
スイッチコントロール
スイッチコントロールは、アクセシビリティ要素を読み上げ順に沿って順番にスキャンします。適切なグループ化とカスタムアクションは、使いやすさに不可欠です。
- 関連するコンテンツを
.accessibilityElement(children: .combine)でグループ化し、スキャン停止位置を減らしてください。 - すべてのスキャン対象に意味があり、操作可能である必要があります。VoiceOver で非表示になる装飾要素は、スイッチコントロールでも非表示になります。
- スイッチコントロールのユーザーは、スワイプして削除する操作、長押し、複数指のジェスチャを実行できません。代わりに、これらの操作を
.accessibilityAction(named:)のカスタムアクションとして公開してください。スイッチコントロールでは、これらの操作がメニューとして表示されます。 - 標準とは異なるヒット領域を持つカスタムコントロールでは、タップ可能な領域が正確に反映されるよう、
accessibilityFrameを設定してください(ポイントスキャンモード用)。
カスタムアクションとグループ化の例については、references/a11y-patterns.md を参照してください。
キーボードでのフルアクセス
フルキーボードアクセス(iOS/iPadOS 13.4 以降)を使うと、ユーザーはハードウェアキーボードでアプリを操作できます。
すべてのコントロールに到達でき、ラベルが付いており、フォーカスが視覚的に示され、タッチなしで操作できるかを監査します。Tab/方向キーによるフォーカス移動、.focusable()、@FocusState、focusSection()、シーンにフォーカスされた値、tvOS のフォーカス、および UIFocusGuide の実装は focus-engine に委ねます。
- すべてのインタラクティブ要素にキーボードで到達し、操作できます。
- 移動順序が論理的で、フォーカスが閉じ込められません。
- あらゆるコントラスト設定と文字サイズ設定で、フォーカスインジケーターが見える状態を保ちます。
- ジェスチャーだけで実行できる操作には、キーボードで操作できる代替手段があります。
- アプリのショートカットが、Cmd+C、Cmd+V、Cmd+Tab などのシステム定義のショートカットを上書きしません。
フルキーボードアクセスの監査項目については、references/a11y-patterns.md を参照してください。
移動順序
要素の順序とグループ化は、VoiceOver のスワイプ順、スイッチコントロールのスキャン、音声コントロールのオーバーレイ、およびフルキーボードアクセスのレビューで、視覚的な順序とタスクの順序に従う必要があります。ラベルの欠落や重複、行内の子要素が多すぎること、非表示のカスタムコントロール、フォーカストラップ、タスクの順序と異なる順序のグループを確認します。キーボードまたは方向キーによるフォーカス移動の仕組みは focus-engine に委ねます。
アクセシビリティアシスタント(iOS 18 以降)
アクセシビリティアシスタントは、認知障害のあるユーザー向けに簡素化されたインターフェースを提供します。アプリはこのモードに対応する必要があります。
// Check if Assistive Access is active (iOS 18+)
@Environment(\.accessibilityAssistiveAccessEnabled) var isAssistiveAccessEnabled
var body: some View {
if isAssistiveAccessEnabled {
SimplifiedContentView()
} else {
FullContentView()
}
}
主なガイドライン:
- 視覚的な複雑さを減らします。コントロールを減らし、タップ領域を大きくし、ナビゲーションを簡素化します。
- ラベルや説明には、明確で文字どおりの表現を使います。
- 一度に提示する選択肢の数を最小限に抑えます。
- 「設定」>「アクセシビリティ」>「アクセシビリティアシスタント」で有効にしてテストします。
UIKit のアクセシビリティパターン
カスタム UIKit ビューでは、意味のある要素、ラベル、値、特性、アクションを公開し、insert/remove で特性を変更し、カスタムオーバーレイをモーダルにし、適切な読み上げ、レイアウト変更、または画面変更の通知を使用します。UIKit の完全な例については、references/a11y-patterns.md を読み込んでください。
AppKit のアクセシビリティパターン
標準の AppKit コントロールを優先してください。カスタムの NSView または仮想要素では、適切な NSAccessibility のロール、ラベル、値、アクション、および状態変更通知を公開します。NSAccessibilityElement とカスタムコントロールの例については、references/a11y-patterns.md を読み込んでください。
アクセシビリティのカスタムコンテンツ
スワイプの停止位置を増やさずに有用な補足情報を提示するには、.accessibilityCustomContent を使用します。高い重要度は、自動的に読み上げるべきコンテンツにのみ指定してください。SwiftUI、UIKit、AppKit の例については、references/a11y-patterns.md を参照してください。
App Store のアクセシビリティ栄養表示
App Store のアクセシビリティ栄養表示、製品ページでの表示、または App Store Connect のアクセシビリティに関する回答については、references/nutrition-labels.md をお読みください。
表示を推奨する前に、該当するデバイスタイプで、その機能を使ってユーザーが一般的なすべてのタスクを完了できることを示す証拠を求めてください。一般的なタスクとアクセシビリティ機能を対応させた構造化マトリックスを使用し、音声のみのコンテンツに字幕が関係する場合はメディアの書き起こしを含めます。また、App Store のアクセシビリティに関する回答は正確さを保つ必要があり、マーケティング上の表示として扱ってはならないことを明示してください。
アクセシビリティのテスト
手動テスト
- アクセシビリティインスペクタ:ラベル、特性、コントラストをシミュレータとデバイスで監査します。
- VoiceOver テスト:設定 > アクセシビリティ > VoiceOver で有効にします。スワイプジェスチャですべての画面を操作します。
- Voice Control テスト:設定 > アクセシビリティ > Voice Control で有効にします。「Show Names」と「Show Numbers」の両方を声に出して確認します。名前では読み上げ可能なラベルを検証し、番号では名前が重複している、欠落している、または不自然な場合でも、表示されているすべての操作対象にアクセスできることを検証します。
- フルキーボードアクセスのテスト:設定 > アクセシビリティ > キーボード > フルキーボードアクセス で有効にします。Tab キーですべての画面を操作し、すべての操作要素にフォーカスが当たることを確認します。
- スイッチコントロールのテスト:設定 > アクセシビリティ > スイッチコントロール で有効にします。スキャン順が論理的で、ジェスチャベースの操作にカスタムアクションが表示されることを確認します。
- ダイナミックタイプ:設定 > アクセシビリティ > 画面表示とテキストサイズ > さらに大きな文字 で、すべての文字サイズをテストします。
XCTest による自動テスト
安定したアクセシビリティ識別子を使って XCUIElement の値を検索し、存在すること、有効/選択状態、意味のあるラベル/値をアサートします。また、テスト環境でフォーカスを取得できる場合は hasFocus も確認します。閉じた後のフォーカス復帰、すべてのモーダル終了経路、ジェスチャの代替手段を網羅します。UI 自動化は、VoiceOver、Voice Control、スイッチコントロール、キーボード、ダイナミックタイプ、コントラスト、視差効果を減らす、透明度を下げるの各テストを補完するものであり、置き換えるものではありません。XCTest の例については references/a11y-patterns.md を読み込んでください。
よくある間違い
| 間違い | 修正方法 |
|---|---|
| 特性の割り当てによって動作が上書きされる | UIKit の特性を追加/削除するか、SwiftUI のアクセシビリティ特性修飾子を使用します。 |
| 閉じた後にフォーカスが失われる | アクセシビリティフォーカスをトリガーに戻します。 |
| 行によってスワイプの停止箇所が過剰に増える | 関連する子要素を意図的にグループ化します。 |
| ラベルでコントロールの種類が繰り返される、またはアイコンの意味が欠落している | 簡潔で読み上げやすいアクション/名前を使います。特性によって種類が読み上げられます。 |
| 動き、文字サイズ、コントラスト、または透明度が固定されている | 対応するアクセシビリティ設定と、状況に応じて変化するテキストスタイルに対応します。 |
| ターゲットが小さい、または色だけで示されている | 44×44 のターゲットに加えて、テキスト、形状、またはアイコンの意味を提供します。 |
| カスタムオーバーレイがモーダルになっていない | モーダルの意味付け、エスケープ操作、フォーカス復帰を提供します。 |
レビューチェックリスト
よく使われるタスクごとに、以下の基準を満たす証拠を記録します。
- 意味付け:コントロールが簡潔な名前、正しいロール/状態/値を公開し、装飾的なコンテンツ、色だけで示されるコンテンツ、ジェスチャのみのコンテンツ、調整可能なコンテンツに代替手段がある。
- ナビゲーション:グループ化と移動順が意図的に設定されている。モーダルでモーダル動作とエスケープ動作が提供され、フォーカスが開始したコントロールに戻る。
- 入力:Voice Control の名前が読み上げ可能で一意であり、Show Names/Numbers の両方が機能する。スイッチコントロールでジェスチャの代替手段が提供され、フルキーボードアクセスに到達不能なコントロール、トラップ、上書きされたシステムショートカットがない。
- 適応:ダイナミックタイプ、視差効果を減らす、透明度を下げる、コントラストを上げる、文字を太くする、44x44 ポイントのターゲットが、代表的なレイアウトと状態で検証されている。
- 自動化:XCTest が安定した識別子、状態、可能な場合はフォーカス、すべてのモーダル終了経路を網羅し、支援技術を用いた手動テストの代わりになっていない。
- 並行性:分離境界を越えるアクセシビリティ値と通知ペイロードが
Sendableである。 - 表明内容:App Store の各アクセシビリティ申告が、申告対象のすべてのデバイス種別について、よく使われるタスクの証拠マトリックスによって裏付けられている。
参考資料
- references/a11y-patterns.md — SwiftUI と UIKit の修飾子の例、グループ化、カスタムアクション、ローター、ダイナミックタイプ
- references/nutrition-labels.md — App Store のアクセシビリティ栄養表示:合否基準を含む現在のカテゴリ
- references/media-accessibility.md — 字幕、音声解説、AVMediaCharacteristic、SDH