React NativeでスムーズなスワイプUIを実装する - ReanimatedSwipeableの使い方 -
はじめに
エックスポイントワンでエンジニアをしているseiyaです。
最近個人でメッセージアプリを開発しており、メッセージアプリでよく見かける、スワイプして削除やアクションを表示するUIを実装しました。
iOSのメールアプリやLINEなどで採用されている、直感的で素早い操作を実現するインタラクションです。

この記事では、react-native-gesture-handlerの「ReanimatedSwipeable」を使って、滑らかなスワイプUIを実装する方法を紹介します。
ReanimatedSwipeableとは
ReanimatedSwipeableは、react-native-gesture-handlerが提供するスワイプ操作用のコンポーネントです。 React NativeのFlatListやScrollViewと組み合わせて使用でき、メッセージアプリやタスク管理アプリでよく見るスワイプUIを簡単に実装できます。

SwipeableではなくReanimatedSwipeableを選んだ理由
react-native-gesture-handlerには、通常のSwipeableコンポーネントも存在します。しかし、公式ドキュメントで以下のような記載があります。
日本語訳:このコンポーネントは非推奨です。reanimated版を使用してください。
そのため、本記事ではReanimatedSwipeableを使用します。
Github:
ドキュメント:
実装方法
ライブラリのインストール
npm install react-native-gesture-handler react-native-reanimated
アプリのルートコンポーネントをGestureHandlerRootViewでラップします。
import { GestureHandlerRootView } from 'react-native-gesture-handler';
export default function App() {
return (
<GestureHandlerRootView style={{ flex: 1 }}>
{/* your app content */}
</GestureHandlerRootView>
);
}
基本的な使い方
最もシンプルな実装例です。
import ReanimatedSwipeable from 'react-native-gesture-handler/ReanimatedSwipeable';
import Reanimated, {
SharedValue,
useAnimatedStyle
} from 'react-native-reanimated';
import { Text, View } from 'react-native';
function RightAction(prog: SharedValue<number>, drag: SharedValue<number>) {
const containerStyle = useAnimatedStyle(() => {
return {
transform: [{ translateX: drag.value + 80 }]
};
});
return (
<Reanimated.View
style={[
containerStyle,
{
width: 80,
backgroundColor: '#FF3B30',
justifyContent: 'center',
alignItems: 'center'
}
]}
>
<Text style={{ color: 'white', fontSize: 14, fontWeight: '600' }}>
削除
</Text>
</Reanimated.View>
);
}
export default function SwipeableItem() {
return (
<ReanimatedSwipeable renderRightActions={RightAction}>
<View style={{ padding: 20, backgroundColor: 'white' }}>
<Text>スワイプしてみてください</Text>
</View>
</ReanimatedSwipeable>
);
}
スワイプ対象のコンポーネントを ReanimatedSwipeable でラップし、スワイプ後の処理(例えばRightAction)を指定します。
ポイント
renderRightActions:右からスワイプした時に表示されるコンポーネント
renderLeftActions:左からスワイプした時に表示されるコンポーネント
drag.value:スワイプの進行度を表すSharedValue
useAnimatedStyle:アニメーションスタイルを定義
worklet:UIスレッドで動作させるためのディレクティブ
主要なプロパティ
スワイプの動作を制御するプロパティ
friction(摩擦係数)
スワイプ距離に対して、見た目の動きがどれくらい遅れるかを指定する数値です。
指の動きとパネルの動きが完全に同期するのは1です。
overshootFriction(オーバーシュート時の摩擦係数)
スワイプの重さを制御します。値が大きいほどスワイプが重く感じられます。
ネイティブアプリのような自然な感覚を得るには8以上を推奨
overshootLeft(左方向のオーバーシュート許可)
アクションパネルを超えて左に引っ張ることを許可するか
rightThreshold(右側の閾値)
スワイプを「開いた状態」とみなす閾値(ピクセル単位)です。
デフォルトはパネル幅の半分ですが、10などの小さい値にしておくと、少しスワイプしただけで開くのでおすすめです。
コールバックプロパティ
onSwipeableWillOpen
スワイプが開くアニメーションが始まる直前に発火します。
onSwipeableOpen
スワイプが完全に開いた時に発火します。
onSwipeableClose
スワイプが完全に閉じた時に発火します。
ハマったポイント:複雑なスワイプ操作の状態管理
スワイプUIの実装で最も苦労したのは、複数のスワイプアイテムがある場合の状態管理です。以下の要件を満たす必要がありました。
- 他のアイテムをスワイプしたら、開いているスワイプを閉じる
- スワイプ中にそのアイテムをタップしたら、スワイプを閉じる(画面遷移しない)
- スワイプ中に他のアイテムをタップしたら、スワイプを閉じる(画面遷移しない)
- スワイプしていない状態でアイテムをタップしたら、画面遷移する
- スワイプ中に画面をタップしたら、スワイプを閉じる
これらの要件を満たすために、前述のコールバックプロパティと状態管理を組み合わせて実装しました。
具体的には
onSwipeableWillOpen で他のスワイプを閉じる
onSwipeableOpen/onSwipeableClose でスワイプ状態を管理
useState でローカル状態を持ち、タップ時の挙動を制御
useRef で現在開いているスワイプを親で管理
TouchableWithoutFeedback で空白部分のタップを検知
これにより、ユーザーの直感的な操作に対応した、快適なスワイプUIを実現できました。
まとめ
ReanimatedSwipeableを使うことで、メッセージアプリやタスク管理アプリで見かけるスワイプUIを簡単に実装できます。
ReanimatedSwipeableの特徴
- UIスレッドで動作し、滑らかなアニメーション
- 豊富なコールバックプロパティで柔軟な制御
- FlatList/ScrollViewとの相性が良い
- renderRightActions/renderLeftActionsでアクションをカスタマイズ
通常のSwipeableとの違い
公式で非推奨となったSwipeableの代わりに、Reanimatedを使ったReanimatedSwipeableを使うことで、より高いパフォーマンスと滑らかなアニメーションが実現できます。
タップよりも素早く直感的に操作できるスワイプUIは、ユーザーの操作頻度が高いアプリに特に効果的です。ぜひ試してみてください!
おわりに
弊社ではモバイルアプリをReact Native + Expoで開発しています。
開発チームでは一緒に開発してくれるエンジニアの仲間を大絶賛募集中です!
少しでも興味を持っていただけた方は、ぜひ以下のリンクからご連絡ください!
Discussion