RORK LABEN
GROWTH — Rorkは月間74.3万訪問・成長率85%と、利用が伸び続けていますMAX — Rork MaxはネイティブSwiftアプリを生成し、iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますMAX — AR/LiDARスキャン・Metalの3Dゲーム・Live Activities・HealthKit・Core MLなど、React Nativeでは届きにくい領域に踏み込めますSTACK — 通常のRorkはReact Native(Expo)でiOSとAndroidを同時に生成し、非エンジニアでも実機アプリを作れますPRICE — 料金は無料から用意され、有料プランは月$25から、Rork Maxは月$200ですMARKET — Gartnerは2026年末までに新規アプリの75%が低コード/ノーコード製になると予測していますGROWTH — Rorkは月間74.3万訪問・成長率85%と、利用が伸び続けていますMAX — Rork MaxはネイティブSwiftアプリを生成し、iPhone・iPad・Watch・TV・Vision Pro・iMessageに対応しますMAX — AR/LiDARスキャン・Metalの3Dゲーム・Live Activities・HealthKit・Core MLなど、React Nativeでは届きにくい領域に踏み込めますSTACK — 通常のRorkはReact Native(Expo)でiOSとAndroidを同時に生成し、非エンジニアでも実機アプリを作れますPRICE — 料金は無料から用意され、有料プランは月$25から、Rork Maxは月$200ですMARKET — Gartnerは2026年末までに新規アプリの75%が低コード/ノーコード製になると予測しています
記事一覧/開発ツール
開発ツール/2026-07-04中級

「続きを読む」を出すべきか、テキストの実測で決める — Rork(Expo)の折りたたみ表示設計

商品説明やレビュー本文を3行で畳み「続きを読む」を出す実装で、短い文にまでトグルが付いてしまう問題を解決します。onTextLayout で実際の行数を測り、はみ出す時だけトグルを出す設計を、iOS と Android の差、展開アニメーション、文字サイズ拡大への配慮まで動くコードでまとめます。

Rork483Expo128React Native193テキスト表示個人開発174

プレミアム記事

商品説明の下に「続きを読む」を付けたはずが、たった一行の短い説明にまでトグルが並んでしまう。個人開発で作っていた壁紙アプリの詳細画面で、私自身このちぐはぐさに手が止まったことがあります。長い本文は畳みたい、けれど短い本文にトグルは要らない。この「出す・出さない」を、文字数のような当てにならない目安ではなく、実際に描画された行数で決めたい、というのが今回の話です。

React Native(Rork が生成する標準のアプリはこの土台の上に乗ります)の Text は、numberOfLines を指定すれば末尾を「…」で切り詰めてくれます。ただ、切り詰めが起きたかどうかを教えてはくれません。トグルを正しく出すには、レンダリングされた本文が本当に指定行数を超えているかを、こちら側で測る必要があります。

文字数で判定すると必ずどこかで外れる

最初に思いつくのは「本文が120文字を超えたらトグルを出す」といった閾値です。手軽ですが、これは早晩ずれます。日本語と英語では1行に載る文字数がまるで違いますし、絵文字や URL、改行の入り方でも変わります。同じ100文字でも、横に長い端末では2行に収まり、iPhone の小さな画面では4行になります。文字数は「画面上で何行になるか」をまったく保証しません。

私は一度この閾値方式で出して、日本語では丁度よく見えたのに、英語ロケールに切り替えた瞬間に大量の空振りトグルが出てヒヤリとしました。判定の基準を「入力の長さ」ではなく「描画の結果」に移すのが、遠回りに見えて確実でした。

onTextLayout で「描画された行数」を受け取る

React Native の Text には onTextLayout というコールバックがあります。テキストが実際にレイアウトされたあと、各行の情報を配列で渡してくれます。行数はこの配列の長さで分かります。

肝心なのは、測るときは numberOfLines を付けないことです。付けたまま測ると、レイアウト結果もその行数に丸められてしまい、「本来は5行だが3行に切られた」のか「もともと3行ちょうど」なのかを区別できません。そこで、いったん制限なしで一度だけ測り、その結果を見てトグルの要否を決める、という順番にします。

import { useState, useCallback } from 'react';
import { Text, Pressable, View, type TextLayoutEventData, type NativeSyntheticEvent } from 'react-native';
 
const COLLAPSED_LINES = 3;
 
type Props = { children: string };
 
export function ExpandableText({ children }: Props) {
  // needsToggle: 本文が3行を超えるか(=トグルを出すか)
  // measured: 一度だけ測り終えたか
  const [needsToggle, setNeedsToggle] = useState(false);
  const [measured, setMeasured] = useState(false);
  const [expanded, setExpanded] = useState(false);
 
  const onTextLayout = useCallback(
    (e: NativeSyntheticEvent<TextLayoutEventData>) => {
      if (measured) return; // 展開後の再レイアウトで上書きしない
      const lineCount = e.nativeEvent.lines.length;
      setNeedsToggle(lineCount > COLLAPSED_LINES);
      setMeasured(true);
    },
    [measured],
  );
 
  return (
    <View>
      {/* 測定パス: 画面外に制限なしで一度だけ描画して行数を得る */}
      {!measured && (
        <Text
          onTextLayout={onTextLayout}
          style={{ position: 'absolute', opacity: 0, left: 0, right: 0 }}
          accessibilityElementsHidden
          importantForAccessibility="no-hide-descendants"
        >
          {children}
        </Text>
      )}
 
      {/* 表示パス: 折りたたみ時だけ numberOfLines を効かせる */}
      <Text numberOfLines={expanded ? undefined : COLLAPSED_LINES}>
        {children}
      </Text>
 
      {needsToggle && (
        <Pressable
          onPress={() => setExpanded((v) => !v)}
          hitSlop={8}
          accessibilityRole="button"
          accessibilityLabel={expanded ? '本文を折りたたむ' : '本文の続きを読む'}
        >
          <Text style={{ color: '#2563eb', marginTop: 4 }}>
            {expanded ? '閉じる' : '続きを読む'}
          </Text>
        </Pressable>
      )}
    </View>
  );
}

測定用の Text を絶対配置+透明で画面外に一度だけ置き、行数を得たら消す。表示用の Text は折りたたみ時のみ numberOfLines を効かせる。この二枚重ねが、切り詰めの有無を正しく知りつつ、ちらつきを最小に抑える形でした。測定用要素はスクリーンリーダーから隠す指定を必ず添えます。読み上げが本文を二重に拾わないためです。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
onTextLayout で描画後の実際の行数を測り、本文が3行を超える時だけ「続きを読む」を出す判定を、動くコードで実装できるようになります
numberOfLines を付けたまま測ると常に3行に丸められてしまう罠を避け、iOS と Android で測り方が食い違う理由と回避策が分かります
展開・折りたたみの LayoutAnimation が Android で無反応になる原因と、VoiceOver・文字サイズ200%の利用者に配慮したトグル設計の判断軸が身につきます
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

開発ツール2026-06-27
無料プレビューがスクショで丸ごと持ち出される前に — Rork/Expo でスクリーンショットと画面収録を検知して目隠しする設計
Rork/Expo アプリで有料プレビュー画像をスクリーンショットと画面収録から守る実装。expo-screen-capture の限界、isCaptured のネイティブ監視、iOS/Android の差を踏まえた目隠し設計を解説します。
開発ツール2026-06-20
Rork が直せるバグと自分で直すバグを見分ける — エクスポートコードのトリアージ手順
Rork が自力で直すバグと、エクスポートしたReact Native/Expoコードを自分で手当てすべきバグを切り分けるトリアージ手順を、動くコードとともに整理しました。
開発ツール2026-06-30
Rork アプリにホーム画面長押しのクイックアクションを足す — 動的項目とコールド起動の取りこぼしを防ぐ
アイコン長押しで出るクイックアクションを Rork(Expo)アプリに実装する設計です。静的・動的項目の使い分け、iOS と Android の差、そしてコールド起動でアクションがルーター準備前に届いて取りこぼす問題を、保留と再生で防ぐ実装手順までまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →