
-
あらゆるユーザー向けにMacアプリのアクセシビリティを高める方法
macOSのパワーと柔軟性を最大限に活用したアクセシビリティ機能を統合する方法を解説します。VoiceOverやVoice Controlをサポートする方法、ビューのレイアウトを改善する方法、支援技術によるコンテンツのナビゲーションの方法など、より発展的な内容を学ぶことができます。
関連する章
リソース
関連ビデオ
WWDC24
WWDC21
-
このビデオを検索
こんにちは Nicholasです Accessibilityチームのエンジニアです アクセシビリティは 作成したアプリを多くの人に 利用してもらうために 非常に重要です 今日は基本だけでなく Macアプリの アクセシビリティを高める方法を説明します Macアプリは主にキーボードとマウスで 操作するよう設計されており 高密度のユーザーインターフェイスと 強力なマルチタスク機能があります 今日はこのMac特有の性質による アクセシビリティ関連の 重要な検討事項についてお話しします
まず アクセシビリティを高めるような アプリのレイアウトを検討し アプリ内のナビゲーションを高速化し アプリの操作のアクセシビリティを 強化します Macのアプリにはコンテンツ等を表示する スペースが十分にあります インターフェイスのレイアウトを わかりやすく直感的なものにするには その要素がアクセシビリティ技術で どう表示されるかを 考えることが重要です SwiftUIは アプリを構成する個々のビューを アクセシビリティ要素として表します アクセシビリティ要素には アクセシビリティ技術で アプリを理解し操作するために必要な 情報が含まれています アクセシビリティ要素に慣れていなければ WWDC 2024のSwiftUIのアクセシビリティの 最新情報をご視聴ください 今 テキスト文書の編集用の MacアプリをSwiftUIで構築しています
サイドバーでは ページを切り替えたり 重要なページに しおりを付けたりできます
コントロールのあるインスペクタでは 選択したテキストを書式設定できます また スタイルプリセットのリストを使うと 複数の書式オプションを 同時に適用できます
本当に良いアプリにしたいので すべての人が使えるものに しなければなりません そのため 初めからアクセシビリティの 基本事項を実装し VoiceOverを使用する新機能を テストしてきました
VoiceOverは 画面読み上げ機能なので さまざまな視力の人が アプリを使えるようになります VoiceOverを使うと インターフェイスを 音声で聞くことができます VoiceOverはこのように聞こえます また点字での表示も可能です VoiceOverを使うと アプリの アクセシビリティを効果的にテストできます VoiceOverは アプリのアクセシビリティ情報を フル活用するからです VoiceOverがうまく機能すれば 音声コントロールや スイッチコントロールなど 他のアクセシビリティ技術とも うまく連携できるでしょう
Macでは VoiceOverは主に キーボードショートカットで操作します たとえばショートカットで 画面上の次の要素や前の要素に移動できます このショートカットを使えば VoiceOverの焦点を 1要素ずつ移動して その説明を 聞くことができます
「最小化、ボタン、フルスクリーン、 ボタン、ツールバー、 テキスト文書、サイドバー、表、 垂直スプリッター、 テキストの編集、テキスト末尾への挿入、 垂直スプリッター、 フォーマットインスペクタ、スクロール領域」 画面上でマウスをさっと動かすだけの操作も VoiceOverのユーザーは キーを何回も 押すことになる場合があります
バックグラウンドでは VoiceOverは SwiftUIによる 次のアクセシビリティ要素に移行しています
VoiceOverでのナビゲーションを 高速化するため SwiftUIでは アクセシビリティ要素をコンテナの アクセシビリティ要素にグループ化します
コンテナのフォーカスは キーボードで操作できます Macのデフォルトでは VoiceOverはコンテナ単位で移動するため アプリ内を移動しながら 必要なときだけ コンテナにフォーカスするほうが高速です Macのアクセシビリティが iPhoneやiPadなど 他のプラットフォームと異なるのは主に Macに入れ子のコンテナがあるためです それにより アクセシビリティ要素の ツリー構造が生成され アプリのインターフェイスを表します アプリでは 関連する要素を コンテナにグループ化して VoiceOverでのナビゲーションを 高速化できます
ただし同時に 入れ子のコンテナのレベルが 多くなりすぎないように注意してください アプリで要素を見つけることが難しくなり コンテナへの フォーカスの繰り返しにかかる時間が 長くなるからです
アプリ内のコンテナを絞り込むには アクセシビリティ要素の子修飾子を使います
修飾子をビューに適用すると 指定された動作によって ビューとそのサブビューが どのように表現されるが決まります 動作のオプションは3つあります
Containはビューを アクセシビリティコンテナとして表現します サブビューはコンテナ内の アクセシビリティ要素です
Combineはビューとそのサブビューを すべての属性とアクションをマージする 1アクセシビリティ要素として表現します Ignoreはビューを 1アクセシビリティ要素として表現し この要素はサブビューを完全に無視します
VoiceOverでアプリをテストしていたとき いくつか再確認したい点に気づきました まず 右側のフォーマットインスペクタで VoiceOverを1つずつ移動します 「フォーマットインスペクタの スクロール領域、 22項目、見出しスタイル、 、ボタン、 、ボタン、 、ボタン、 、ボタン、 ボールドトグルボタン」 ここでボールドトグルボタンに 到達するには スタイルプリセットを すべて経由する必要があることが わかりました スタイルプリセットは VStackを使用して表示されます
VStackで アクセシビリティ要素の子修飾子を Contain動作とともに使用して コンテナを作成します アクセシビリティラベル修飾子を使用して このコンテナに名前を付けることもできます
ここでもう一度 右側のフォーマットインスペクタで VoiceOverを1つずつ移動させます 「フォーマットインスペクタ、 スクロール領域、15項目、見出しスタイル、 スタイルプリセット、グループ、 ボールド、トグルボタン」 いいですねすべての項目を経由する必要はありません 探しているものだけで十分です このコンテナ内でのナビゲーションが 機能することを確認するため VoiceOverをこの スタイルプリセットの コンテナにフォーカスさせ 1つずつ移動します
「スタイルプリセット内、グループ、 8項目、ボタン、、 ボタン、、ボタン、 、ボタン」 うまくいきましたが VoiceOverは 各スタイルプリセットのタイトルと 「Apply」ボタンに別個に フォーカスしています
スタイルプリセットはタイトルとボタンの 各ビューを含むHスタックとして表されます
アクセシビリティ要素の子修飾子と ageスタックで組み合わせた動作を使用して 1つのアクセシビリティ要素を作成します これによりタイトルビューとボタンビューの プロパティがマージされます
ここでもう一度 スタイルプリセットコンテナ内で VoiceOverを1つずつ移動させます 「スタイルプリセット内、グループ、 4項目、、ボタン、 、ボタン、 、ボタン、 、ボタン」 これで 各プリセットを1つの要素として 確認できます これなら移動も容易で 理解しやすいです
この構造で調整できるもう1つの側面は 要素の順序です この例では が の前にありますが VoiceOverにはを 先に読んで欲しいとします たくさんの本のタイトルを ざっと頭に入れたいからです アクセシビリティ要素の順序を変更するには accessibilitySortPriority修飾子を 使用します
本のタイトルビューの優先度を高くすると 最初にソートされます デフォルトでは ビューの並べ替え優先度は0です 優先度の等しいビューが 視覚的な位置に 基づいてソートされます これで まず本のタイトルと 次に 著者と説明が読み上げられます アプリでVoiceOverをテストする際 この順序が不適当なら accessibilitySortPriority修飾子を 使用します
アクセシビリティ構造を 改良できたので ローターを使用して さらにナビゲーションを進めます
アプリのブックマーク機能を使用すると ページリストにざっと目を通して ブックマーク済みのページを確認できます でも VoiceOverを使っているユーザーは ブックマークを確認するためだけに すべてのページを移動する必要があります 「2ページ ブックマークあり」 「3ページ」 「4ページ」 「5ページ ブックマークあり」 「6ページ」 「7ページ」「8ページ ブックマークあり」 「9ページ」「10ページ」 この操作を 見える場合と同様に 簡単にするには VoiceOverでも ブックマーク済みの ページだけを移動できる機能が必要です
これにはローターを使います VoiceOverを使用するユーザーにとって ローターは動作の円滑化に不可欠です ローターを使用すると アプリ内で素早く ジャンプできるビューのコレクションや テキスト範囲を定義できます ブックマークのローターを マイページリストに 追加するには accessibilityRotor修飾子を使用して Bookmarksという名前を付けます 条件付きでページを宣言します Bookmarksローターに含めるページです これでVoiceOverを サイドバーのリストに合わせると キーボードショートカットで ローターメニューを開き すべてのブックマークを素早く読み上げて 目的のブックマークページに ジャンプできます 「ブックマークメニュー」 「2ページ」 「5ページ」 「5ページ ブックマークあり」
アクセシビリティローターは VoiceOverを使用するユーザーにとって 時間がかかったり複雑だったりした操作を 素晴らしい体験に変化させます
VoiceOverでアプリを操作するときは VoiceOverフォーカスと呼ばれるものを 動かしています VoiceOverのような アクセシビリティ技術では キーボードとは別の 独自のフォーカスを維持します
macOSおよびiOS 26では accessibilityDefaultFocus修飾子を 使用すれば アプリでVoiceOverのような アクセシビリティ技術の 最初のフォーカスを提案できます 新しいシーンが表示されると SwiftUIがビューを提案し 修飾子がフォーカスされて 一方 アクセシビリティ技術は引き続き ユーザーの目的に基づいて 最善の決定を下します
これで アプリ内の要素への 移動が簡単になったので その操作も簡単に できるようにしたいと思います このアプリでは ページのサムネールに ポインタを 合わせると出るボタンで ブックマークできる 機能を追加しました これの良い点は ボタンが不要な時には サムネールの表示が 遮られないことです ただし VoiceOverのユーザーは ポインタを動かさないため ボタンにアクセスできません
アプリで ポインタを 要素上に移動する必要がある操作や トラックパッドジェスチャーの実行は 誰もが行えるとは限りません そうした操作を見つけて実行する 別の方法を提供する必要があります
幸い SwiftUIなら これを簡単な方法で行えます ビューにはVoiceOverのような アクセシビリティ技術が アクセシブルな方法で実行できる アクションを追加できます アクセシビリティアクションをビューに 追加するには その修飾子を使用します 修飾子を使って ページリストのビューに ブックマークアクションを追加します 非常に簡単です
次に VoiceOverで ページのサムネールをフォーカスし 新しいアクセシビリティアクションを 使用してみます 「3ページ」 「メニュー」 「2つの項目があります」 「別の項目を表示します」 「3ページをブックマーク」 「3ページをブックマーク」
キーボードショートカットを使って メニューを開き ページを簡単にブックマークできました 音声コントロールなど 他の アクセシビリティ技術もこのアクションに 依存しています
アプリの操作感を改善する 別の 簡単な方法がキーボードショートカットです 一般的なタスクを キーボードで操作できることは 優れた機能であると同時に アクセシビリティにも大きな影響を与え 特にマウスを使用できない人の役に立ちます 最後に 独自のカスタムコントロールを 作成する場合 他のコントロールのように組み込みの アクセシビリティ情報がありません SwiftUIでコントロールを簡単に アクセス可能にする方法については 「SwiftUI Accessibility: Beyond the basics」をご確認ください WWDC 2021です 皆さんもぜひ MacでVoiceOverを使用して アプリ内のコンテナを改良し カスタムローターを追加してみてください そして最後に Accessibility Nutrition Labelを使えば アプリのアクセス性を強調できます 詳細については 「Evaluate your app for Accessibility Nutrition Labels」をご覧ください ありがとうございました
-
-
4:15 - Contain subviews within accessibility container
// Contain subviews within accessibility container import SwiftUI struct ContentView: View { var body: some View { VStack { FirstView() SecondView() } .accessibilityElement(children: .contain) } }
-
4:23 - Combine subviews into one accessibility element
// Combine subviews into one accessibility element import SwiftUI struct ContentView: View { var body: some View { VStack { FirstView() SecondView() } .accessibilityElement(children: .combine) } }
-
4:33 - Hide subviews from accessibility
// Hide subviews from accessibility import SwiftUI struct ContentView: View { var body: some View { VStack { FirstView() SecondView() } .accessibilityElement(children: .ignore) } }
-
5:12 - Contain style presets in accessibility container
// Contain style presets in accessibility container import SwiftUI struct FormattingInspectorView: View { var body: some View { Form { VStack { StylePresetView(type: .title) StylePresetView(type: .heading) StylePresetView(type: .subHeading) StylePresetView(type: .body) } .accessibilityElement(children: .contain) .accessibilityLabel("Style Presets") } } }
-
6:21 - Merge Title View and Button into one accessibility element
// Merge Title View and Button into one accessibility element import SwiftUI struct StylePresetView: View { let preset: StylePreset var body: some View { HStack { PresetTitleView(preset: preset) Button("Apply") { /* ... */ } } .accessibilityElement(children: .combine) } }
-
7:01 - Set the order of accessibility elements
// Set the order of accessibility elements import SwiftUI struct BookDetailsView: View { let book: Book var body: some View { VStack { Text(book.author) Text(book.title) .accessibilitySortPriority(1) DescriptionView(book: book) } .accessibilityElement(children: .combine) } }
-
8:43 - Add an accessibility rotor for bookmarked pages
// Add an accessibility rotor for bookmarked pages import SwiftUI struct PagesView: View { @Binding var pages: [Page] var body: some View { List(pages) { page in PageListItemView(page: page) } .accessibilityRotor("Bookmarks") { ForEach(pages) { page in if page.isBookmarked { AccessibilityRotorEntry(page.title, id: page.id) } } } } }
-
9:33 - Set the default VoiceOver focus
// Set the default VoiceOver focus struct MyView: View { @AccessibilityFocusState(for: .voiceOver) var focusedForVoiceOver var body: some View { FirstView() SecondView() .accessibilityDefaultFocus($focusedForVoiceOver, true) ThirdView() } }
-
10:28 - Add an accessibility action to bookmark the page
// Add an accessibility action to bookmark the page import SwiftUI struct PageListItemView: View { var page: Page var body: some View { VStack() { ThumbnailView(page: page) Text(page.title) } .onHover { /* ... */ } .accessibilityAction(named: page.isBookmarked ? "Remove Bookmark" : "Bookmark") { page.isBookmarked.toggle() } } }
-