Text#accessibilityHint

2025-06-06

なぜ必要か?

通常、スクリーンリーダーはaccessibilityLabelプロパティを読み上げます。accessibilityLabelは、要素の目的や内容を簡潔に説明するためのものです。しかし、accessibilityLabelだけでは要素を操作した結果が明確でない場合があります。

例えば、「戻る」というラベルのボタンがあったとして、それだけでは「前の画面に戻る」のか、「前のステップに戻る」のか、あるいは「履歴を元に戻す」のかが曖昧な場合があります。このような場合にaccessibilityHintを使用することで、より具体的な情報を提供し、ユーザーが安心して操作できるようにします。

accessibilityHint の使い方

Textコンポーネント(またはViewTouchableOpacityなどの他のアクセシビリティ要素)に文字列として設定します。

<Text
  accessible={true} // アクセシビリティ要素として認識させる
  accessibilityLabel="戻る" // 要素の目的を伝える
  accessibilityHint="前の画面に戻ります" // 操作の結果を補足する
  onPress={() => navigateToPreviousScreen()}
>
  Back
</Text>

上記の例では、スクリーンリーダーがこの要素にフォーカスすると、まずaccessibilityLabelの「戻る」が読み上げられ、その後にaccessibilityHintの「前の画面に戻ります」が読み上げられます(ユーザーのスクリーンリーダー設定によって、ヒントの読み上げが有効になっている場合)。

accessibilityLabelaccessibilityHint の違い

  • accessibilityLabel: その要素が「何であるか」を説明します。要素の目的や内容を簡潔に伝えます。
  • 主に、**インタラクティブな要素(ボタンなど)**に対して使用することが推奨されます。単にテキストを表示するだけのTextコンポーネントに設定しても、あまり意味がないことが多いです。
  • accessibilityHintは、ユーザーがスクリーンリーダーの設定で「ヒント」を有効にしている場合にのみ読み上げられます。そのため、accessibilityHintにのみ頼って重要な情報を伝えるべきではありません。
  • accessibilityHint必須ではありませんaccessibilityLabelだけで十分目的が伝わる場合は、無理に設定する必要はありません。情報が多すぎると、かえってユーザーの負担になることがあります。


Text#accessibilityHintに関する一般的なエラーとトラブルシューティング

accessibilityHintが読み上げられない

最も一般的な問題は、せっかく設定したaccessibilityHintがスクリーンリーダー(iOSのVoiceOver、AndroidのTalkBackなど)によって読み上げられないことです。

  • コンポーネントの種類による制限
    一部のネイティブコンポーネントでは、accessibilityHintの動作が期待通りでない場合があります。例えば、iOSのセグメントコントロール(SegmentedControl)の個々のセグメントにヒントが適用されない、といった報告があります。このような場合は、代替手段としてaccessibilityLabelをより詳細にするか、カスタムコンポーネントでアクセシビリティを制御する必要があります。
  • TextInputコンポーネントでの挙動の違い
    TextInputにおいて、placeholdervalueが設定されている場合、accessibilityLabelaccessibilityHintが読み上げられないという既知の問題が過去に存在しました。これは特にAndroidで顕著でした。最新のReact Nativeバージョンでは改善されている可能性がありますが、もし遭遇した場合は、accessibilityValueプロパティの使用や、状況に応じてカスタムのアクセシビリティイベントを発火させる(AccessibilityInfo.announceForAccessibility()など)ことも検討します。
  • accessibilityLabelだけで十分だとOSが判断している
    特にAndroidでは、accessibilityLabelが十分に要素の目的を伝えていると判断された場合、accessibilityHintが省略されることがあります。これは厳密にはエラーではありませんが、意図した動作と異なる場合があります。より具体的なヒントが必要な場合は、accessibilityLabelとのバランスを考慮してください。
  • iOS/AndroidのOS設定でヒントの読み上げが無効になっている
    ユーザーはスクリーンリーダーの設定で「ヒント(Hints)」の読み上げをオフにすることができます。開発環境でテストする際は、OSのアクセシビリティ設定でヒントの読み上げが有効になっているか確認してください。
    • iOS (VoiceOver)
      設定 > アクセシビリティ > VoiceOver > 詳細 > ヒントの読み上げ
    • Android (TalkBack)
      設定 > アクセシビリティ > TalkBack > 設定 > 詳細設定 > 要素のタイプと操作ヒントを読み上げる
  • accessible={true}が設定されていない
    accessibilityHintは、そのコンポーネントがアクセシビリティ要素として認識されている場合にのみ機能します。Textコンポーネント自体や、それを囲む親のViewなどにaccessible={true}が設定されているか確認してください。
    <Text
      accessible={true} // これが重要!
      accessibilityLabel="写真"
      accessibilityHint="写真をタップすると拡大表示されます"
    >
      夕焼けの写真
    </Text>
    

accessibilityLabelとaccessibilityHintの情報の重複・冗長

  • 問題点
    accessibilityLabelaccessibilityHintに同じような情報を含めると、スクリーンリーダーが冗長な読み上げをしてしまい、ユーザーの負担になります。「戻るボタン、前の画面に戻ります」のようなケースでは許容されますが、「送信ボタン、送信します」のようにほとんど同じ内容だと問題です。

複雑なUI要素におけるアクセシビリティの課題

  • 対処法
    • accessible={true}の位置
      子要素を個別に読み上げさせたい場合は、親にaccessible={true}を設定しないか、子要素それぞれにaccessible={true}accessibilityLabelを設定します。
    • accessibilityRoleの活用
      要素の役割(ボタン、リンク、見出しなど)を適切にaccessibilityRoleで設定することで、スクリーンリーダーがより適切な読み上げをします。
    • accessibilityActionsとonAccessibilityAction
      より複雑なインタラクションが必要な場合、accessibilityActionsで実行可能なアクションを定義し、onAccessibilityActionでそのアクションを処理することで、ユーザーに操作方法を明確に伝えることができます。
    • フォーカス順序
      React Nativeには明示的なフォーカス順序を制御するプロパティは提供されていませんが、要素のDOM(ビュー)構造や配置順序がアクセシビリティのフォーカス順序に影響します。論理的なUIの順序と一致するようにコンポーネントを配置することが重要です。
  • 問題点
    複数の要素がネストされている場合や、カスタムコンポーネントを作成した場合に、意図しない形で要素がグループ化されたり、フォーカス順序が不適切になったりすることがあります。

プラットフォームごとの挙動の違い

  • 対処法
    • 両プラットフォームでのテスト
      開発中は必ずiOSとAndroidの両方の実機(またはエミュレーター/シミュレーター)でスクリーンリーダーを有効にしてテストを行い、挙動の違いを把握しましょう。
    • React Nativeのバージョンアップ
      古いバージョンのReact Nativeを使用している場合、アクセシビリティ関連のバグが修正されている可能性があるため、最新バージョンへの更新を検討してください。
  • 問題点
    iOSとAndroidでアクセシビリティの挙動が微妙に異なることがあります。特にaccessibilityHintは、AndroidのネイティブAPIに直接対応するものがなかったため、過去にはReact Native側でcontentDescriptionにヒントを結合するなどの工夫がされていました。これにより、意図しない読み上げ順序や省略が発生することがありました。
  1. スクリーンリーダーを有効にする
    • iOS
      設定 > アクセシビリティ > VoiceOver をオン。
    • Android
      設定 > アクセシビリティ > TalkBack をオン。
  2. 対象の要素にフォーカスを当てる
    スクリーンリーダーのジェスチャーを使って、問題の要素にフォーカスを当ててみてください。
  3. accessible={true}の確認
    要素(または親要素)にaccessible={true}が設定されているか確認してください。これが最も基本的な要件です。
  4. accessibilityLabelの確認
    accessibilityHintよりも先に読み上げられるaccessibilityLabelが正しく設定されているか、意図した内容になっているか確認してください。
  5. OSのアクセシビリティ設定の確認
    ヒントの読み上げがOSレベルで無効になっていないか確認してください。
  6. 情報の重複・冗長性の確認
    accessibilityLabelaccessibilityHintの内容が重複していないか見直してください。
  7. コンポーネントの構造を確認
    要素が不必要にネストされていないか、アクセシビリティの観点から適切な要素構造になっているか確認してください。
  8. 他のアクセシビリティプロパティとの干渉
    accessibilityRoleaccessibilityStateなど、他のアクセシビリティプロパティがaccessibilityHintの読み上げに影響を与えていないか確認してください。
  9. React Nativeのバージョン
    使用しているReact Nativeのバージョンが古すぎないか確認してください。既知のバグが修正されている可能性があります。
  10. GitHub Issueの確認
    同じような問題がReact NativeのGitHubリポジトリのissueで報告されていないか検索してみるのも有効です。解決策やワークアラウンドが見つかる場合があります。


基本的な使用例

最もシンプルな例として、ボタンのような役割を持つTextコンポーネントにaccessibilityHintを設定するケースです。

import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Alert } from 'react-native';

const AccessibilityExample = () => {
  const handlePressGoBack = () => {
    Alert.alert('ナビゲーション', '前の画面に戻ります。');
    // 実際のアプリケーションでは、ここにナビゲーションロジックが入ります
  };

  const handlePressLike = () => {
    Alert.alert('アクション', 'いいね!しました。');
  };

  const handlePressMoreInfo = () => {
    Alert.alert('情報', '詳細情報が表示されます。');
  };

  return (
    <View style={styles.container}>
      {/* 1. 基本的なボタンの例 */}
      <TouchableOpacity
        accessible={true}
        accessibilityLabel="戻る"
        accessibilityHint="前の画面に戻ります"
        onPress={handlePressGoBack}
        style={styles.button}
      >
        <Text style={styles.buttonText}>戻る</Text>
      </TouchableOpacity>

      {/* 2. アイコン付きボタンの例(Textコンポーネント自体にヒントを設定) */}
      <TouchableOpacity
        accessible={true} // TouchableOpacityはデフォルトでaccessible={true}だが、明示的に記述
        accessibilityLabel="いいね"
        accessibilityHint="この項目にいいねを付けます"
        onPress={handlePressLike}
        style={styles.iconButton}
      >
        <Text style={styles.iconText}></Text>
      </TouchableOpacity>

      {/* 3. Textコンポーネント自体にonPressとヒントを設定する例 */}
      {/* Textコンポーネント自体にonPressを設定する場合、accessible={true}を明示することが重要です */}
      <Text
        accessible={true}
        accessibilityRole="button" // ボタンとしての役割を明示
        accessibilityLabel="詳細情報"
        accessibilityHint="このトピックに関する追加情報を表示します"
        onPress={handlePressMoreInfo}
        style={styles.infoText}
      >
        詳細はこちら
      </Text>

      {/* 4. アクセシビリティヒントが不要な場合の例 */}
      <Text style={styles.normalText}>
        これは通常のテキストです。特に操作はありません。
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
    backgroundColor: '#f0f0f0',
  },
  button: {
    backgroundColor: '#007bff',
    paddingVertical: 10,
    paddingHorizontal: 20,
    borderRadius: 5,
    marginBottom: 15,
  },
  buttonText: {
    color: '#ffffff',
    fontSize: 16,
    fontWeight: 'bold',
  },
  iconButton: {
    backgroundColor: '#28a745',
    padding: 10,
    borderRadius: 25,
    marginBottom: 15,
  },
  iconText: {
    fontSize: 24,
  },
  infoText: {
    fontSize: 16,
    color: '#0056b3',
    textDecorationLine: 'underline',
    padding: 10, // タップ可能な領域を広げる
    marginBottom: 15,
  },
  normalText: {
    fontSize: 16,
    color: '#333',
  },
});

export default AccessibilityExample;

コードの説明

  1. アイコン付きボタンの例 (TouchableOpacity内のText):

    • この例では、アイコン(絵文字)を表示するTextコンポーネントを含むTouchableOpacityを使用しています。
    • TouchableOpacityaccessibilityLabelaccessibilityHintを設定することで、内部のTextの内容に関わらず、一貫したアクセシブルな情報を提供できます。
  2. アクセシビリティヒントが不要な場合の例:

    • 単にテキストを表示するだけのTextコンポーネントには、accessibilityHintは不要です。これは操作を伴わないため、ヒントを提供しても意味がありません。

このコードを実機(またはエミュレーター/シミュレーター)で実行し、スクリーンリーダー(iOSのVoiceOver、AndroidのTalkBack)を有効にして試してみてください。

  • 「これは通常のテキストです。」にフォーカスした場合
    • VoiceOver/TalkBack: 単純にテキストの内容が読み上げられます。ヒントは読み上げられません。
  • 「」ボタンにフォーカスした場合
    • VoiceOver/TalkBack: 「いいね、ボタン、この項目にいいねを付けます」といった形で読み上げられます。
  • 「戻る」ボタンにフォーカスした場合
    • VoiceOver/TalkBack: 「戻る、ボタン、前の画面に戻ります」といった形で読み上げられます。


accessibilityLabelの表現を工夫する

最も直接的な代替方法です。accessibilityHintが提供する情報をaccessibilityLabel自体に含めることで、一つのプロパティで完結させることができます。

利点

  • accessibilityHintがユーザー設定で読み上げオフにされている場合でも、情報が伝わる。
  • シンプルで実装が容易。

欠点

  • accessibilityLabelは要素の「目的」を伝える役割が強いため、「操作結果」のニュアンスが伝えにくい場合がある。
  • accessibilityLabelが長くなりすぎると、ユーザーにとって読み上げが煩わしくなる可能性がある。


// accessibilityHint を使用する場合
<Text
  accessible={true}
  accessibilityLabel="保存"
  accessibilityHint="変更内容をサーバーに保存します"
  onPress={handleSave}
>
  保存
</Text>

// accessibilityLabel だけで完結させる場合
<Text
  accessible={true}
  accessibilityLabel="変更内容をサーバーに保存するボタン" // より詳細なラベル
  onPress={handleSave}
>
  保存
</Text>

accessibilityRoleを適切に設定する

accessibilityRoleは、要素のUIタイプや目的をスクリーンリーダーに伝えます。これにより、ユーザーはその要素がどのような振る舞いを期待できるかを理解しやすくなります。ヒントを直接伝えるわけではありませんが、要素の役割を明確にすることで、結果的にユーザーの操作理解を助けます。

利点

  • accessibilityHintが不要な場合でも、要素の目的を伝える上で非常に重要。
  • 要素の役割を明確にすることで、スクリーンリーダーが適切なインタラクション方法(「ボタン」「リンク」「チェックボックス」など)をユーザーに伝える。


// テキストがボタンのように見えるが、HTMLの<a>タグのように動作する場合
<Text
  accessible={true}
  accessibilityRole="link" // リンクの役割を明示
  accessibilityLabel="プライバシーポリシー"
  onPress={openPrivacyPolicy}
>
  プライバシーポリシーを読む
</Text>

// チェックボックスのラベルとヒント
<View accessible={true} accessibilityRole="checkbox" accessibilityState={{ checked: isChecked }} onPress={() => setIsChecked(!isChecked)}>
  <Text accessibilityLabel="通知を受け取る" accessibilityHint="新しいメッセージが届いたときに通知します">
    通知を受け取る
  </Text>
</View>

AccessibilityInfo.announceForAccessibility()を使用する

これは、特定のアクションの結果や、動的に変化するUIの状態をスクリーンリーダーにアナウンスさせるための強力なメソッドです。accessibilityHintが要素にフォーカスが当たったときに読み上げられるのに対し、announceForAccessibility()は任意のタイミングでメッセージを読み上げさせることができます。

利点

  • UIの特定の領域が更新されたことをユーザーに知らせる「ライブリージョン」のような使い方もできる。
  • ユーザーが操作を行った直後など、リアルタイムなフィードバックを提供するのに最適。

欠点

  • 読み上げるタイミングを適切に管理する必要がある。
  • 頻繁に使いすぎると、ユーザーエクスペリエンスを損なう可能性がある。


import React from 'react';
import { View, Text, Button, Alert, AccessibilityInfo } from 'react-native';

const DynamicAnnouncementExample = () => {
  const [count, setCount] = React.useState(0);

  const handleIncrement = () => {
    const newCount = count + 1;
    setCount(newCount);
    // カウントが更新されたことをスクリーンリーダーにアナウンス
    AccessibilityInfo.announceForAccessibility(`カウントが ${newCount} になりました。`);
  };

  const handleSend = () => {
    // データ送信後の完了メッセージをアナウンス
    Alert.alert('送信完了', 'データが正常に送信されました。');
    AccessibilityInfo.announceForAccessibility('データが正常に送信されました。');
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text style={{ fontSize: 24, marginBottom: 20 }}>現在のカウント: {count}</Text>
      <Button title="カウントを増やす" onPress={handleIncrement} />

      <View style={{ marginTop: 50 }}>
        <Button title="データを送信" onPress={handleSend} />
      </View>
    </View>
  );
};

export default DynamicAnnouncementExample;

accessibilityActionsとonAccessibilityAction

これはより複雑なインタラクションが必要な場合に有効です。要素がサポートするカスタムアクションを定義し、スクリーンリーダーユーザーが特定のアクション(例: スワイプ、ダブルタップなど)でその操作を実行できるようにします。accessibilityHintは「これを行うとこうなる」という情報を提供しますが、accessibilityActionsは「これらのアクションを実行できます」という情報を提供します。

利点

  • ネイティブのアクセシビリティジェスチャーと連携できる。
  • 複雑なUI要素に対して、より多くの操作オプションを提供する。

欠点

  • すべてのケースで必要となるわけではない。
  • 実装が複雑になる可能性がある。


import React from 'react';
import { View, Text, Switch, StyleSheet } from 'react-native';

const CustomSwitchExample = () => {
  const [isEnabled, setIsEnabled] = React.useState(false);

  const toggleSwitch = () => {
    setIsEnabled(previousState => !previousState);
  };

  const handleAccessibilityAction = (event) => {
    switch (event.nativeEvent.actionName) {
      case 'activate': // スクリーンリーダーのタップアクション
        toggleSwitch();
        break;
      case 'toggle': // カスタムで定義したトグルアクション
        toggleSwitch();
        break;
      default:
        break;
    }
  };

  return (
    <View style={styles.container}>
      <Text
        accessible={true}
        accessibilityRole="switch"
        accessibilityLabel="設定を有効にする"
        accessibilityHint="タップすると設定のオン/オフが切り替わります" // ここでヒントも併用可能
        accessibilityState={{ checked: isEnabled }}
        accessibilityActions={[{ name: 'toggle', label: 'オン/オフを切り替える' }]} // カスタムアクション
        onAccessibilityAction={handleAccessibilityAction}
        style={styles.customSwitchText}
      >
        設定: {isEnabled ? 'オン' : 'オフ'}
      </Text>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  customSwitchText: {
    fontSize: 20,
    padding: 15,
    borderWidth: 1,
    borderColor: '#ccc',
    borderRadius: 8,
  },
});

export default CustomSwitchExample;

この例では、Switchコンポーネントの代わりにTextを使ってカスタムのスイッチを作成するケースを想定しています。accessibilityHintaccessibilityActionsを併用することで、より詳細な情報と操作方法を提供しています。

Text#accessibilityHintは特定の情報を伝えるための強力なツールですが、アクセシビリティ全体を考慮する際には、以下の点を念頭に置き、状況に応じて最適な方法を選択・組み合わせることが重要です。

  • accessibilityActions: より複雑なインタラクションや複数の操作オプションを提供する。
  • AccessibilityInfo.announceForAccessibility(): 動的なコンテンツ変更や即時のフィードバックをアナウンスする。
  • accessibilityRole: 要素の種類を定義し、スクリーンリーダーに適切な操作方法を促す。
  • accessibilityLabel: 要素の目的を簡潔に伝える。