Files
jrshikoku/docs/actionsheet-gesture-spec.md

4.6 KiB

ActionSheet Gesture Specification

Overview

JR Shikoku mobile appのActionSheet(ニュースリリース、各列車情報など)のジェスチャー実装仕様です。


Core Behavior

Swipe-to-Dismiss (スワイプで閉じる)

Platform 動作 方法
iOS (Phone) シート下方向スワイプで閉じる isModal={true} + gestureEnabled={true}
iOS (iPad) スワイプ無効 isModal={false} → 通常のViewとして表示
Android スワイプ無効 isModal={false}

isModalの役割

  • isModal={true} にするとActionSheet内部で<Modal>ラッパーが適用されます
  • iOSのModalはOSレベルのswipe-to-dismissジェスチャーを組み込みでサポートしています
  • gestureEnabled={true} でそのジェスチャーを有効化します
  • iPadでは物理的に画面が larg いため、モーダル化しません(通常Viewとして表示)

Height Control

EachTrainInfo (共用パターン)

const maxHeight = useSheetMaxHeight();
// ↓
containerStyle={{ maxHeight }}

useSheetMaxHeight()フックが以下を判定して高さを計算:

shortSide(デバイス短辺) maxHeight値 意味
≥ 600 (iPad etc.) undefined 全画面表示、高さ制限なし
< 600 (Phone) deviceHeight * 0.75 画面の高さの75%

NewsReleaseInfo (個別実装)

const sheetHeight = windowDimen.height * 0.8;
// ↓
containerStyle={{ height: sheetHeight }}
  • Phoneのみ画面高さの80%固定
  • shortSide判定はしない点で差異あり

Platform-Specific Layout

Android

containerStyle={{
  paddingBottom: insets.bottom,  // SafeArea対応
  useBottomSafeAreaPadding={true},
}}

AndroidはSafeAreaパディングが必要です。 Bottom barの領域を避けます。


ScrollView inside ActionSheet

react-native-actions-sheet内部のScrollView使用(注意)

import { ScrollView } from "react-native-actions-sheet";  // ← これ使う
  • nestedScrollEnabled={true} はAndroid用(nested scrolling有効化)
  • ScrollViewコンテンツとActionSheetのジェスチャーが干渉しないように重要

Layout Structure (テンプレート)

// ActionSheet
containerStyle={{ height: ..., borderTopLeftRadius: 5, borderTopRightRadius: 5 }}
CustomHeaderComponent={<></>}           // カスタムヘッダーなし
gestureEnabled={true}                   // iOSでswipe-to-dismiss有効
isModal={Platform.OS === "ios" && !Platform.isPad}

→ Content
  → DragHandle (ドラッグハンドル)
  → Title (タップでスクロールトップ)
  → ScrollView                         ← ネスト可能なコンテンツのみ
      → NewsReleaseInfoBox / EachTrainInfoCore
  → BottomButton (固定配置、ScrollView外)   ← 押せるボタンは必ず外側

ボタンの配置ルール

  • 重要な操作ボタン(「公式でもっと見る」etc.)はScrollViewの外に配置
  • ScrollView内にあるとスクロールで隠れ、クリック不可になる
  • ActionSheetの下部、SafeAreaパディングの直上に固定する

Back Handler (Androidのみ)

useEffect(() => {
  if (Platform.OS === "android") {
    const backAction = () => true;   // default prevent(スワイプ無効化)
    const backHandler = BackHandler.addEventListener(
      "hardwareBackPress", backAction
    );
    return () => backHandler.remove();
  }
}, []);

Androidでハードウェアバックキーを押下時にスワイプを無効化します。


Gesture Flow (iOS)

ユーザー操作                ActionSheet内部処理
────────                  ────────────────
シートを下方向にスワイプ → Modalがgesture検知
  ↓                       ↓
スワイプ距離閾値超過       ↓(閉じる判定)
  ↓                       ↓
Modal-dismiss実行         sheet ref close + onCloseコールバック

Key Points Summary

  1. ジェスチャー有効化: gestureEnabled={true} は必須
  2. iOSモーダル化: isModal={Platform.OS === "ios" && !Platform.isPad}
  3. Androidパディング: useBottomSafeAreaPadding + containerStyle.pb 両方必要
  4. ScrollView配置: ActionSheetコンテンツ内のスクロールにはライブラリ版(react-native-actions-sheet)を使用
  5. ボタン配置: 重要ボタンはScrollView外、画面下部固定