😀

【iOS】VoiceOver対応の実装方法 - 基本的な使い方

に公開

最低限の実装だけで自然な読み上げを作る(SwiftUI)

動作確認編はこちら → 【iOS】VoiceOverの動作確認方法 - シミュレータですぐ試せる

VoiceOver 対応は少し難しそうに見えるかもしれませんが、
実は やるべきことが決まっている ので、ポイントさえ押さえれば十分自然な読み上げが実現できます。

まずは “やらなくていいこと” と “やるべきこと” を分けるところから始めましょう。

🎯 基本の考え方

VoiceOver のために伝える情報は次の 3 つだけです。

  • label:その要素が何を意味するか
  • hint:操作したときに何が起こるか
  • value:現在の状態・数値

この 3 つを“必要なところにだけ”付ければ十分です。

まずは「何もしなくていい」要素

SwiftUI の標準コンポーネントは、アクセシビリティがかなり整っているため、
まずは 手を入れなくてよい部分 を明確にします。

基本的にはそのままでOK

  • Text → テキストをそのまま読んでくれる
  • Button → ラベルを理解して「◯◯、ボタン」と読まれる
  • Toggle → オン/オフを自然に伝えてくれる
  • TextField → 多くの場合、プレースホルダーをラベルとして扱う

ここに無理に手を加える必要はありません。

手を入れるべき要素(ここを押さえれば十分)

VoiceOver 対応の仕上がりに大きく影響するのは次の 3 つです。

  • Image / SF Symbols(意味のある画像)
  • Button を使っていない“ボタン風の View”
  • 複数の要素を 1 つのまとまりとして伝えたい View

ここを改善するだけで、多くの UI は自然に扱えるようになります。

.accessibilityLabel() — 意味を伝えるためのラベル

ラベルは 見た目ではなく“意味”を伝える のが大切です。

意味のある画像にラベルを付ける

Image("yarn")
    .accessibilityLabel("編み物のイラスト")

アイコン+テキストをひとまとまりにする

VStack {
    Image(systemName: "heart.fill")
    Text("お気に入り")
}
.accessibilityElement(children: .combine)
.accessibilityLabel("お気に入り")

combine を使うことで、
2つの要素を1つの読み上げ対象として自然に扱える ようになります。

.accessibilityHint() — 必要なときだけ使う

ヒントは VoiceOver に 毎回読み上げられる ため、使いどころが重要です。

付ける価値がある例

Button("作品を記録する") { }
    .accessibilityHint("新しい作品を追加します")

不要な例(自明)

Button("作品を記録する") { }
    .accessibilityHint("作品を記録します") // これは不要

“押したあと何が起きるか想像しにくい場面” に絞って使います。

.accessibilityValue() — 数値や状態を明確にする

  • 進捗
  • 件数
  • 選択状態
  • 日付

などに適しています。

ProgressView(value: progress)
    .accessibilityLabel("進行状況")
    .accessibilityValue("\(Int(progress * 100))%")

.accessibilityHidden(true) — 装飾は読ませない

意味を持たない装飾画像は読み上げても情報にならないため、
VoiceOver の利用者にとって負担になることがあります。

Image("background")
    .accessibilityHidden(true)

ただし、状態を表すアイコン(エラーなど) は label を付けて伝えます。

.accessibilityElement(children:) — 複数要素をまとめて扱う

複数の要素が「見た目としても意味としても 1 つのまとまり」なら、
VoiceOver にとっても 1 つとして扱われたほうが理解しやすくなります。

よく使うのは .combine / .contain です。

実践例:複数要素を “まとまり” として扱う

アイコン+見出し+説明+ボタンの構成を例に

VStack(spacing: 24) {
    Image(systemName: "sparkles")
        .accessibilityHidden(true) // 装飾的なため読み上げない

    Text("はじめての作品を記録しましょう")
        .font(.headline)

    Text("編んだ作品を管理できます")
        .font(.body)

    Button("作品を記録する") { }
}
.accessibilityElement(children: .contain)

VoiceOver の読み上げ順は自然に:

  1. 見出し
  2. 説明文
  3. ボタン

という流れになります。

最低限の判断基準(これだけ覚えればOK)

  • 意味がある → label
  • 自明 → hint は付けない
  • 状態・数値 → value
  • 装飾 → hidden
  • まとまりを 1 つとして扱いたい → combine / contain

無理に全部使う必要はなく、
“必要な場所に必要な情報だけ” を追加するイメージで十分です。

関連記事・参考資料

動作確認の方法はこちら

https://zenn.dev/yumemi_inc/articles/bc24e31dfa5160

Apple公式ドキュメント

WWDC動画

ゆめみ

Discussion