React Native Text#nativeID 徹底解説:アクセシビリティとiOS連携の鍵

2025-06-06

nativeIDは、React Nativeのコンポーネント(特にViewTextなど)に付与できるプロパティの一つです。このプロパティの主な目的は、ネイティブコード(iOSのSwift/Objective-C、AndroidのJava/Kotlin)から特定のビュー要素を識別できるようにすることです。

React Nativeで開発するアプリは、最終的にネイティブのUIコンポーネントにレンダリングされます。通常、JavaScript側で要素を操作する際はReactのRefなどを使用しますが、より低レベルなネイティブの機能にアクセスしたり、ネイティブモジュールと連携したりする際に、JavaScript側で定義したUI要素をネイティブコードから参照したい場合があります。その際にnativeIDが役立ちます。

主な用途

  1. アクセシビリティ (Accessibility): 特に重要なのがアクセシビリティです。例えば、スクリーンリーダー(視覚障害者向けの読み上げ機能)を使用する際に、あるTextInputに対して、そのラベルとなるTextコンポーネントを関連付けたい場合があります。nativeIDaccessibilityLabelledByプロパティを組み合わせることで、この関連付けを行うことができます。

    例:

    <View>
      <Text nativeID="formLabel">入力フィールドのラベル</Text>
      <TextInput
        accessibilityLabel="入力"
        accessibilityLabelledBy="formLabel"
      />
    </View>
    

    この例では、TextInputにフォーカスが当たった際に、スクリーンリーダーが「入力、編集ボックス、入力フィールドのラベル」のように読み上げることが期待されます。

  2. InputAccessoryView との連携 (iOS): iOSでは、キーボードの上にカスタムツールバーを表示するInputAccessoryViewというコンポーネントがあります。このInputAccessoryViewを特定のTextInputに関連付けるためにnativeIDが使われます。

    const inputAccessoryViewID = 'uniqueID';
    
    <ScrollView>
      <TextInput
        inputAccessoryViewID={inputAccessoryViewID}
        onChangeText={setText}
        value={text}
        placeholder={'ここに入力してください…'}
      />
    </ScrollView>
    <InputAccessoryView nativeID={inputAccessoryViewID}>
      <Button onPress={() => setText('')} title="テキストをクリア" />
    </InputAccessoryView>
    

    ここでは、TextInputinputAccessoryViewIDInputAccessoryViewnativeIDを同じ値にすることで、両者を紐付けています。

  3. ネイティブコードからの要素特定: 非常に稀なケースですが、JavaScriptレイヤーで定義されたReact Nativeのビュー(例: Text)を、直接ネイティブコード側からIDを使って検索・操作したい場合に利用されることがあります。nativeIDを付与することで、ネイティブビューヒエラルキー内でその要素を一意に識別する手がかりとなります。

注意点

  • デバッグ/テストIDとの違い: testIDという似たようなプロパティがありますが、これは主に自動テストツール(例: Detox)から要素を特定するために使用されます。nativeIDはよりネイティブの連携やアクセシビリティの目的で使われます。
  • ネイティブの実装: nativeIDは、React Nativeが内部で各プラットフォームのネイティブビューにIDを割り当てるためのヒントとして使用されます。JavaScript側で単にIDを付与するだけでなく、そのIDがネイティブ側でどのように利用されるかを理解しておくことが重要です。
  • 一意性: nativeIDは、そのスクリーン(またはビューヒエラルキー)内で一意である必要があります。重複するIDを使用すると予期せぬ動作を引き起こす可能性があります。


nativeID の重複

エラーの症状

  • デバッグ中に、ネイティブ側で要素を識別しようとした際に、期待する要素が見つからないか、複数の要素がヒットしてしまう。
  • InputAccessoryView などが正しく表示されない、または複数の TextInput で共有しようとした場合に問題が発生する。
  • アクセシビリティツール(VoiceOverやTalkBackなど)が正しく要素を読み上げない、または意図しない要素を読み上げる。

原因

  • 同じnativeIDを複数のコンポーネントに割り当てている。nativeIDはその名の通り、ネイティブレイヤーでの一意な識別子として機能するため、重複は許容されません。特にリストレンダリングなどで動的にコンポーネントを生成する際に、ユニークなIDを生成し忘れることがあります。

トラブルシューティング

  • accessibilityLabelledByで使用する場合、参照先のnativeIDが本当にその画面内で一意であることを確認してください。
  • 動的にnativeIDを生成する場合は、ループインデックスやユニークなキー(UUIDなど)を組み合わせて一意性を保証します。
    {items.map((item, index) => (
      <Text key={item.id} nativeID={`itemLabel-<span class="math-inline">\{item\.id\}\-</span>{index}`}>
        {item.name}
      </Text>
    ))}
    
  • コード全体を検索し、重複するnativeIDがないか確認します。

アクセシビリティ関連の誤解や不適切な使用

エラーの症状

  • ユーザーが期待する操作ができない(例: キーボードナビゲーションで特定の要素にフォーカスできない)。
  • accessibilityLabelledByが機能しない。
  • スクリーンリーダーが要素を読み飛ばす、または意味不明な読み上げをする。

原因

  • 複雑なUIにおいて、アクセシビリティの論理的な流れが設計されていない。
  • nativeIDを付与しただけで、その要素がアクセシブルになるわけではないという誤解。
  • nativeIDは設定しているが、accessibilityLabelledByなどの関連プロパティが正しく設定されていない。

トラブルシューティング

  • アクセシビリティテストツールの活用
    iOSのVoiceOverやAndroidのTalkBackといったネイティブのアクセシビリティ機能を実際に使用して、アプリをテストすることが非常に重要です。シミュレーターやエミュレーターだけでなく、実際のデバイスでのテストも行いましょう。また、Accessibility Inspectorなどのデバッグツールも役立ちます。
  • 役割(accessibilityRole)の指定
    要素の役割を明確にすることで、スクリーンリーダーがより適切な読み上げを行います。
    <Text nativeID="headerTitle" accessibilityRole="header">
      セクションタイトル
    </Text>
    
  • accessible プロパティの確認
    特定のビューがスクリーンリーダーで読み上げられるようにしたい場合は、accessible={true}を設定する必要がある場合があります。ただし、多くのCore Componentはデフォルトでアクセシブルです。
  • accessibilityLabelledBy の正しい使用
    accessibilityLabelledBy を使用する際は、参照する nativeID が正確に一致していることを確認します。
    // NG: nativeIDとaccessibilityLabelledByが一致しない
    // <Text nativeID="myLabel">ラベル</Text>
    // <TextInput accessibilityLabelledBy="labelForInput" />
    
    // OK: nativeIDとaccessibilityLabelledByが一致する
    <Text nativeID="myLabel">ラベル</Text>
    <TextInput accessibilityLabelledBy="myLabel" />
    

InputAccessoryView との連携問題 (iOS)

エラーの症状

  • InputAccessoryView が予期せず消える。
  • 複数の TextInput で同じ InputAccessoryView を共有しようとすると、一部の TextInput でしか表示されない。
  • キーボードの上にカスタムツールバーが表示されない。

原因

  • React Nativeのバージョンによる問題。特定のバージョンで、複数の TextInput が同じ inputAccessoryViewID を共有する際にバグがある場合があります(GitHubのIssueなどで報告されていることがあります)。
  • InputAccessoryViewnativeIDTextInputinputAccessoryViewID が一致していない。
  • Modal との干渉
    Modal コンポーネントを使用している場合に、InputAccessoryView が正しく表示されないことがあります。これは、Modal が新しいネイティブウィンドウを作成するため、InputAccessoryView がそのコンテキストを失うことがあるためです。
    • ModalpresentationStyle プロパティを overFullScreen に設定することで解決することがある、という報告があります。
    • Modal の表示/非表示に応じて、InputAccessoryView を再レンダリングするような状態管理を検討します。
  • 複数 TextInput の共有
    複数の TextInput で同じ InputAccessoryView を使用する場合、特にReact Nativeの特定のバージョンで問題が発生することがあります。その場合は、以下の代替案を検討します。
    • 各 TextInput にユニークな InputAccessoryView を持たせる
      コードの重複は増えますが、最も確実な方法です。
    • 状態管理で表示を切り替える
      InputAccessoryView 自体は常にレンダリングしておき、どの TextInput がフォーカスされているかに応じて、InputAccessoryView の中身や表示/非表示を制御するロジックを実装します。
    • 最新のReact Nativeバージョンへのアップデート
      既知のバグであれば、新しいバージョンで修正されている可能性があります。
  • IDの一致確認
    InputAccessoryViewnativeID と、関連付けたい TextInputinputAccessoryViewID が完全に一致していることを確認します。
    const accessoryId = 'myInputAccessory';
    
    <InputAccessoryView nativeID={accessoryId}>
      {/* カスタムツールバーのコンポーネント */}
    </InputAccessoryView>
    
    <TextInput inputAccessoryViewID={accessoryId} />
    
  • ネイティブデバッグツール
    iOSの場合はXcodeのView Debugger、Androidの場合はAndroid StudioのLayout Inspectorを使用して、ネイティブビューのヒエラルキーを確認し、nativeIDがネイティブレイヤーでどのように認識されているかを直接調べることができます。
  • React Native Debugger
    React Native Debuggerを使ってコンポーネントツリーを確認し、nativeIDプロパティが正しく反映されているか視覚的に確認できます。
  • コンソールログの活用
    console.log() を使って、nativeIDが期待通りに設定されているか、動的に生成されるIDがユニークであるかなどを確認します。


Text#nativeID のプログラミング例

nativeID は主に以下の2つの主要なユースケースで利用されます。

  1. アクセシビリティ (accessibilityLabelledBy との連携)
  2. iOS の InputAccessoryView との連携

それぞれの例を見ていきましょう。

例1: アクセシビリティ (accessibilityLabelledBy との連携)

この例では、Text コンポーネントに nativeID を付与し、それを TextInput コンポーネントの accessibilityLabelledBy プロパティで参照することで、スクリーンリーダー(視覚補助機能)が TextInput の目的をより明確に読み上げるようにします。

import React, { useState } from 'react';
import { View, Text, TextInput, StyleSheet, Button, Alert } from 'react-native';

const AccessibilityExample = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = () => {
    Alert.alert('ログイン', `ユーザー名: ${username}\nパスワード: ${password}`);
  };

  return (
    <View style={styles.container}>
      {/* ユーザー名入力フィールドのセクション */}
      <View style={styles.inputGroup}>
        {/*
          Text コンポーネントに nativeID を付与
          このIDは、TextInputが何のための入力フィールドであるかをスクリーンリーダーに伝えるために使われます。
        */}
        <Text nativeID="usernameLabel" style={styles.label}>
          ユーザー名:
        </Text>
        <TextInput
          style={styles.input}
          value={username}
          onChangeText={setUsername}
          placeholder="ユーザー名を入力"
          // accessibilityLabelledByTextnativeID を参照
          // これにより、スクリーンリーダーは「ユーザー名: テキストフィールド」のように読み上げます。
          accessibilityLabelledBy="usernameLabel"
          autoCapitalize="none"
        />
      </View>

      {/* パスワード入力フィールドのセクション */}
      <View style={styles.inputGroup}>
        <Text nativeID="passwordLabel" style={styles.label}>
          パスワード:
        </Text>
        <TextInput
          style={styles.input}
          value={password}
          onChangeText={setPassword}
          placeholder="パスワードを入力"
          secureTextEntry
          // ここでも nativeID を参照
          accessibilityLabelledBy="passwordLabel"
        />
      </View>

      <Button title="ログイン" onPress={handleSubmit} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
    backgroundColor: '#f5f5f5',
  },
  inputGroup: {
    marginBottom: 20,
  },
  label: {
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 5,
  },
  input: {
    height: 40,
    borderColor: '#ccc',
    borderWidth: 1,
    borderRadius: 5,
    paddingHorizontal: 10,
    backgroundColor: '#fff',
  },
});

export default AccessibilityExample;

このコードのポイント

  • これにより、スクリーンリーダーを使用しているユーザーは、入力フィールドにフォーカスした際に「ユーザー名: テキストフィールド」や「パスワード: テキストフィールド」といった、より意味のあるコンテキスト情報を受け取ることができます。
  • それぞれの TextInput は、accessibilityLabelledBy プロパティで対応する TextnativeID を参照しています。
  • usernameLabelpasswordLabel というユニークな nativeID を持つ Text コンポーネントがあります。

この例は iOSのみ で動作します。InputAccessoryView は、キーボードの上に表示されるカスタムツールバーを提供します。これを特定の TextInput に関連付けるために nativeID を使用します。

import React, { useState } from 'react';
import {
  View,
  Text,
  TextInput,
  StyleSheet,
  Button,
  Platform,
  InputAccessoryView, // iOS専用
  ScrollView,
} from 'react-native';

const InputAccessoryExample = () => {
  const [textInput1, setTextInput1] = useState('');
  const [textInput2, setTextInput2] = useState('');

  // InputAccessoryView の ID を定義
  const accessoryViewID = 'myCustomKeyboardAccessory';

  // 入力フィールドをクリアする関数
  const clearInput = (setter) => {
    setter('');
  };

  return (
    <View style={styles.container}>
      <ScrollView contentContainerStyle={styles.scrollContent}>
        <Text style={styles.header}>InputAccessoryView 例 (iOSのみ)</Text>

        <View style={styles.inputGroup}>
          <Text style={styles.label}>テキスト入力1:</Text>
          <TextInput
            style={styles.input}
            value={textInput1}
            onChangeText={setTextInput1}
            placeholder="ここに何か入力してください..."
            // iOSの場合、InputAccessoryView との連携に inputAccessoryViewID を使用
            inputAccessoryViewID={accessoryViewID} // nativeIDと同じ値を設定
          />
        </View>

        <View style={styles.inputGroup}>
          <Text style={styles.label}>テキスト入力2:</Text>
          <TextInput
            style={styles.input}
            value={textInput2}
            onChangeText={setTextInput2}
            placeholder="別の入力を試してください..."
            inputAccessoryViewID={accessoryViewID} // 同じ accessoryViewID を使用
          />
        </View>

        {/* スクロール可能にするためのダミーコンテンツ */}
        <View style={{ height: 300, backgroundColor: '#eee', justifyContent: 'center', alignItems: 'center' }}>
          <Text>下にスクロールして、InputAccessoryView がキーボードに追従するか確認してください。</Text>
        </View>
      </ScrollView>

      {/* InputAccessoryView の定義 (iOS専用) */}
      {Platform.OS === 'ios' && (
        <InputAccessoryView nativeID={accessoryViewID}>
          <View style={styles.accessoryContainer}>
            <Button title="テキスト1をクリア" onPress={() => clearInput(setTextInput1)} />
            <Button title="テキスト2をクリア" onPress={() => clearInput(setTextInput2)} />
            <Button title="Done" onPress={() => { /* キーボードを閉じるなどの処理 */ }} />
          </View>
        </InputAccessoryView>
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  scrollContent: {
    padding: 20,
    paddingBottom: 100, // InputAccessoryView のためのスペースを確保
  },
  header: {
    fontSize: 20,
    fontWeight: 'bold',
    marginBottom: 30,
    textAlign: 'center',
  },
  inputGroup: {
    marginBottom: 20,
  },
  label: {
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 5,
  },
  input: {
    height: 50,
    borderColor: '#ccc',
    borderWidth: 1,
    borderRadius: 8,
    paddingHorizontal: 12,
    backgroundColor: '#fff',
    fontSize: 16,
  },
  accessoryContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    alignItems: 'center',
    height: 44,
    backgroundColor: '#f0f0f0',
    borderTopWidth: 1,
    borderTopColor: '#ccc',
    paddingHorizontal: 10,
  },
});

export default InputAccessoryExample;
  • Platform.OS === 'ios' で、iOSでのみ InputAccessoryView をレンダリングするように条件分岐しています。
  • これにより、どちらかの TextInput がフォーカスされた際に、同じ InputAccessoryView (カスタムツールバー) がキーボードの上に表示されるようになります。
  • TextInput には inputAccessoryViewID={accessoryViewID} を設定しています。
  • InputAccessoryView には nativeID={accessoryViewID} を設定しています。
  • accessoryViewID という定数で、InputAccessoryViewTextInput を関連付けるためのIDを定義しています。


アクセシビリティ関連の代替

nativeID が主に accessibilityLabelledBy と組み合わせて使われる場面での代替方法です。

  • accessibilityRole の使用
    要素が果たす役割をスクリーンリーダーに伝えるために accessibilityRole を使用します。これにより、特定のUI要素が何であるか(例: ボタン、ヘッダー、リンクなど)を明確にできます。直接 nativeID の代替とはなりませんが、アクセシビリティの文脈で要素を適切にマークアップする際に重要です。

    <Text accessibilityRole="header" style={styles.sectionHeader}>
      個人情報
    </Text>
    

    利点
    要素のセマンティックな意味を正確に伝え、スクリーンリーダーのユーザー体験を向上させる。 欠点: nativeID のように要素間の明示的な関連付けを提供するものではない。

  • Text のネスト(入れ子構造)
    React Native の Text コンポーネントは、他の Text コンポーネントを入れ子にすることができます。これにより、異なるスタイルを適用したり、セマンティックなグループ化を行ったりできます。視覚的なラベルと入力フィールドが近接している場合、スクリーンリーダーは自動的に関連付けて読み上げることがあります。

    <View style={styles.inputGroup}>
      <Text style={styles.label}>
        ユーザー名:
        <TextInput
          style={styles.input}
          value={username}
          onChangeText={setUsername}
          placeholder="ユーザー名を入力"
          autoCapitalize="none"
        />
      </Text>
    </View>
    

    利点
    nativeID を使う必要がなく、コードが簡潔になる場合がある。 欠点: すべてのプラットフォームやスクリーンリーダーで常に期待通りの読み上げになるとは限らない。特に複雑なUIの場合、アクセシビリティの挙動が予測しづらくなる可能性がある。accessibilityLabelledBy のように明示的な関連付けではないため、より堅牢なアクセシビリティが求められる場合には不十分な場合がある。

  • accessibilityLabel の直接使用
    最も直接的な代替方法です。Text コンポーネントが単独でラベルとして機能する場合や、複雑な構造でない場合は、TextInput などに直接 accessibilityLabel を設定するだけで十分な場合があります。スクリーンリーダーはこのラベルを読み上げます。

    // nativeID を使用しない場合
    <TextInput
      style={styles.input}
      value={username}
      onChangeText={setUsername}
      placeholder="ユーザー名を入力"
      accessibilityLabel="ユーザー名" // 直接ラベルを設定
      autoCapitalize="none"
    />
    

    利点
    コードがシンプルになり、nativeID の重複を気にする必要がない。 欠点: ラベルとなる Text コンポーネントが視覚的に存在する場合、スクリーンリーダーがその TextTextInput の両方を読み上げてしまう可能性があり、冗長になることがある。accessibilityLabelledBy のように、視覚的なラベルとアクセシビリティラベルを紐付ける厳密な意味合いが薄い。

InputAccessoryView 関連の代替 (iOS)

InputAccessoryViewnativeIDinputAccessoryViewID の組み合わせで機能するため、これに対する直接的な代替は非常に限定的です。基本的に、この機能を使う場合は nativeID を使うのが推奨される方法です。

しかし、もし InputAccessoryView が特定の TextInput に常に紐づくのではなく、キーボード表示時に汎用的に表示されるツールバーを想定しているのであれば、以下のようなアプローチが考えられます。

  • KeyboardAvoidingView とカスタムコンポーネントの使用
    これは InputAccessoryView の代替というよりは、Androidを含むクロスプラットフォームでキーボードに追従するUIを実現する方法です。カスタムのツールバーコンポーネントを作成し、KeyboardAvoidingView の内部に配置することで、キーボードの表示に合わせてそのコンポーネントが移動するようにします。

    import React, { useState } from 'react';
    import { View, Text, TextInput, StyleSheet, Button, Platform, KeyboardAvoidingView, ScrollView } from 'react-native';
    
    const CustomToolbar = ({ onClear }) => (
      <View style={toolbarStyles.container}>
        <Button title="クリア" onPress={onClear} />
        <Button title="閉じる" onPress={() => { /* キーボードを閉じる処理など */ }} />
      </View>
    );
    
    const KeyboardAvoidingExample = () => {
      const [text, setText] = useState('');
    
      return (
        <KeyboardAvoidingView
          behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
          style={styles.container}
          keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0} // 必要に応じて調整
        >
          <ScrollView contentContainerStyle={styles.scrollContent}>
            <Text style={styles.header}>KeyboardAvoidingView とカスタムツールバー</Text>
            <Text style={styles.bodyText}>スクロールして、一番下の入力フィールドを試してください。</Text>
            {/* ダミーコンテンツ */}
            {[...Array(10)].map((_, i) => (
              <View key={i} style={styles.dummyItem}>
                <Text>リストアイテム {i + 1}</Text>
              </View>
            ))}
    
            <View style={styles.inputGroup}>
              <Text style={styles.label}>入力フィールド:</Text>
              <TextInput
                style={styles.input}
                value={text}
                onChangeText={setText}
                placeholder="テキストを入力..."
              />
            </View>
          </ScrollView>
    
          {/* キーボードに追従させたいカスタムツールバー */}
          <CustomToolbar onClear={() => setText('')} />
        </KeyboardAvoidingView>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
      },
      scrollContent: {
        flexGrow: 1,
        padding: 20,
        justifyContent: 'flex-end', // コンテンツを下に寄せる
      },
      header: {
        fontSize: 20,
        fontWeight: 'bold',
        marginBottom: 20,
        textAlign: 'center',
      },
      bodyText: {
        marginBottom: 20,
        textAlign: 'center',
      },
      dummyItem: {
        height: 60,
        backgroundColor: '#f0f0f0',
        marginBottom: 10,
        justifyContent: 'center',
        alignItems: 'center',
      },
      inputGroup: {
        marginTop: 20,
        marginBottom: 10,
      },
      label: {
        fontSize: 16,
        fontWeight: 'bold',
        marginBottom: 5,
      },
      input: {
        height: 50,
        borderColor: '#ccc',
        borderWidth: 1,
        borderRadius: 8,
        paddingHorizontal: 12,
        backgroundColor: '#fff',
        fontSize: 16,
      },
    });
    
    const toolbarStyles = StyleSheet.create({
      container: {
        flexDirection: 'row',
        justifyContent: 'space-around',
        alignItems: 'center',
        backgroundColor: '#e0e0e0',
        paddingVertical: 10,
        borderTopWidth: 1,
        borderColor: '#ccc',
      },
    });
    
    export default KeyboardAvoidingExample;
    

    利点
    クロスプラットフォームでキーボード追従UIを実現できる。InputAccessoryView が提供しない柔軟なレイアウトが可能。 欠点: InputAccessoryView のようなネイティブレベルのキーボードとの統合ではないため、一部の挙動(例: キーボードのドラッグでツールバーが連動する)は再現が難しい場合がある。

  • TextInput を InputAccessoryView で直接ラップする
    これは、InputAccessoryView のドキュメントにも記載されている「Sticky text inputs」のシナリオです。InputAccessoryView の中に TextInput を直接含めることで、nativeID を使わずに InputAccessoryView を特定の TextInput に紐付けることができます。ただし、この場合、その InputAccessoryView はその TextInput 専用になります。

    import React, { useState } from 'react';
    import { InputAccessoryView, TextInput, Button, View, Platform, StyleSheet } from 'react-native';
    
    const StickyTextInputExample = () => {
      const [message, setMessage] = useState('');
    
      return (
        <View style={styles.container}>
          {/* 他のコンテンツ */}
          <Text style={styles.bodyText}>メッセージを送信できます。</Text>
    
          {Platform.OS === 'ios' && (
            <InputAccessoryView>
              {/* nativeID を設定しない */}
              <View style={styles.accessoryContainer}>
                <TextInput
                  style={styles.input}
                  value={message}
                  onChangeText={setMessage}
                  placeholder="メッセージを入力..."
                  multiline
                />
                <Button title="送信" onPress={() => { Alert.alert('送信', message); setMessage(''); }} />
              </View>
            </InputAccessoryView>
          )}
        </View>
      );
    };
    
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        justifyContent: 'flex-end', // 下部に固定されるように
        paddingBottom: Platform.OS === 'ios' ? 0 : 20, // Androidの場合の余白
      },
      bodyText: {
        flex: 1,
        textAlign: 'center',
        paddingTop: 50,
      },
      accessoryContainer: {
        flexDirection: 'row',
        alignItems: 'center',
        backgroundColor: '#f0f0f0',
        paddingVertical: 8,
        paddingHorizontal: 10,
        borderTopWidth: 1,
        borderColor: '#ccc',
      },
      input: {
        flex: 1,
        borderColor: '#ccc',
        borderWidth: 1,
        borderRadius: 20,
        paddingHorizontal: 15,
        paddingVertical: 8,
        marginRight: 10,
        backgroundColor: '#fff',
        maxHeight: 120, // マルチライン対応
      },
    });
    
    export default StickyTextInputExample;
    

    利点
    nativeID が不要。特定の TextInput とツールバーが一体化しているようなUIで自然。 欠点: 複数の TextInput で同じツールバーを共有するような複雑なシナリオには向かない。キーボードの表示/非表示に完全に追従させるためのレイアウト調整が必要になる場合がある。

テストID (testID) の使用

nativeID はネイティブ統合やアクセシビリティが主な目的ですが、要素の一意な識別という点では testID も似た役割を持ちます。ただし、testID は主に自動テスト(例: Detox, Appium)で要素を特定するために使用されます。

<Text testID="welcomeText">
  ようこそ!
</Text>

<Button
  testID="loginButton"
  title="ログイン"
  onPress={() => console.log('ログインボタンが押されました')}
/>

利点
テストコードから簡単に要素を特定できる。 欠点: nativeID が持つアクセシビリティや InputAccessoryView との直接的な連携機能は持たない。本番環境のアプリの動作に直接影響を与えることは少ない。

Text#nativeID は、その名の通り「ネイティブID」として特定のネイティブ連携を目的としたプロパティです。特にアクセシビリティの accessibilityLabelledBy や iOS の InputAccessoryView との連携には非常に有効であり、多くの場合、推奨されるアプローチです。

しかし、以下のような場合は代替手段を検討することもできます。

  • 自動テストでの要素識別
    testID
  • 汎用的なキーボード追従UI(Androidも含む)
    KeyboardAvoidingView とカスタムコンポーネント。
  • 単純なアクセシビリティの読み上げ
    accessibilityLabel の直接使用や Text の入れ子。