motion-system について
motion-systemは、iOSチーム向けにアニメーションと触覚フィードバックを調査し、アプリに合った小規模なモーションシステムを提案するスキルです。呼び出し箇所の変更前に承認を求め、実装とコンパイル確認も案内します。
主な機能
- 01アニメーション、時間、触覚、視差効果の監査
- 02操作の目的別に動きを分類
- 0360fpsライブラリで参考例を検索
- 04コード変更前に名前付きの値を提案
- 05Swiftのモーションファイルを実装して型チェック
活用シーン
- 01iOSアプリ内の不統一なアニメーション時間を整理
- 02機能の動きと触覚フィードバックの役割を定義
- 03呼び出し箇所の移行前にモーション案を作成
MCP 対応
60fps MCPサーバーを使い、モーション例の検索とモーションコードの取得を行います。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ 60fps-design/ plugin --skill motion-systemインストール後、Claude Code で/motion-systemを入力して読み込まれたか確認します。
詳細
モーションシステム
同じ種類の場面で常に同じ動きをするアプリは、ひとつのアプリとしてまとまりを感じられます。多くのコードベースでは、動きに一貫性がなくなっていきます。何十種類もの継続時間が使われ、違いを見分けられないスプリングが増え、あらゆる場面で成功時の触覚フィードバックが使われます。あなたの役割は、アプリ独自の優れたモーションと、60fps ライブラリにある実際のアプリを参考にしながら、いくつかの名前付きの値に置き換えることです。
成果物は、提案書と新しいファイルひとつです。ユーザーが提案に同意するまでは、既存の呼び出し箇所を編集しないでください。
1. 最低対応バージョンを確認する
アプリのターゲットの IPHONEOS_DEPLOYMENT_TARGET を確認します。ターゲットで独自の値が設定されている場合、プロジェクト全体の設定より優先されます。どちらの方向に異なる場合も同様です。プロジェクトで定義されている場所を確認してください。.xcodeproj、XcodeGen の project.yml、.xcconfig ファイル、または Package.swift です。提案するすべての API がそのバージョンで利用できる必要があります。手順 5 でコンパイルするために、Swift の言語モードと、デフォルトのアクター分離設定も確認してください。
2. アプリで現在使われているものをすべて洗い出す
コードを検索し、すべてのモーション値と、それに合わせてタイミング調整されているものを見つけます。
- アニメーション:
withAnimation(、.animation(、.spring(、.interactiveSpring、.interpolatingSpring、.smooth、.snappy、.bouncy、.easeIn、.easeOut、.easeInOut、.linear、.default、duration:、.delay(、response:、dampingFraction:。.smooth、.snappy、.bouncyが単独で使われている場合も値です(デフォルトは 0.5 秒です)。これらも数えてください。 - トランジションとエフェクト:
.transition(、contentTransition、matchedGeometryEffect、matchedTransitionSource、phaseAnimator、keyframeAnimator、symbolEffect。 - アニメーションと連動するタイミング:
Task.sleep、asyncAfter、完了ハンドラー。変更のリスクが最も高い箇所です。0.35 秒のアニメーションに合わせたスリープは、アニメーションを変更すると不具合につながります。 - 触覚フィードバック:
sensoryFeedback、UI...FeedbackGeneratorクラス、それらを使うアプリ内ラッパーの呼び出し箇所(呼び出し箇所が 50 件あるHapticManagerは、クラスそのものより重要です)。 - 視差効果を減らす設定:読み取り方法(
accessibilityReduceMotion、isReduceMotionEnabled)をすべて確認します。 - 既存のヘルパー:
Motion、Animation、触覚フィードバックに関する拡張機能やマネージャー。
ファイル単位ではなく、役割ごとに値を分類します。
- フィードバック:押下、切り替え、選択、その場での小さな切り替わり。短くあるべきです。
- 変化:コンテンツの切り替え、行の表示、数値の変化。
- 登場:シート、カード、画面要素の表示。
- 強調:成功や節目など、印象に残るべきまれな場面。
- 触覚フィードバック:用途ごとに使われている種類。
各グループで異なる値の数を数えます。この数が問題の説明になります。「登場には 0.3 秒から 0.7 秒まで、8 種類の異なるタイミングが使われている」のように示します。すべてのモーションが役割を持つわけではありません。視差効果や指に追従するドラッグなどのジェスチャー追跡、またコメントで不可欠だと明記された値は、システムの対象外です。これらは意図的な例外として列挙してください。
既存の仕組みを拡張します。アプリに触覚フィードバックのラッパー(アプリ内のオン/オフ切り替えを備えていることもよくあります)がある場合、新しい触覚フィードバックの役割にも必ずそのラッパーを使い、迂回しないでください。sensoryFeedback を使うと切り替えを迂回してしまいます。既存のモーションヘルパーに不具合がある、または使われていない場合は、その旨を伝え、修正または削除を提案してください。別のヘルパーを並べて追加してはいけません。
3. アプリのトーンを見極める
既存のモーションとデザイン(色、書体、コピーの遊び心の強さ)を確認し、トーンを短い言葉で表します。たとえば、落ち着いていて正確、柔らかく親しみやすい、大胆で遊び心がある、などです。提案するすべての値を、そのトーンに合わせてください。金融アプリにゲームのようなスプリングを使うべきではありません。
4. 実際のアプリを参考にする
参考資料が必要なジョブごとに、60fps_search_shots を1回呼び出します。オブジェクトではなく、動きと雰囲気を説明してください。「画面の読み込み時にセクションが次々とフェードインする、落ち着いた雰囲気」と指定すると到着演出が見つかりますが、「カードが現れる」と指定するとカードのスタックが見つかります。タイトル、概要、intensity、mood を基準に、スコアではなく雰囲気に合うショットを3〜5個選びます。motion タグがスプリング、フェード、クロスフェードを示すショットを優先してください。タイミングが TimelineView やカスタム計算で制御されている場合、読み取れるアニメーション値はありません。雰囲気に合うものが見つからない場合(落ち着いたアプリでは、落ち着いた祝賀演出が見つかることはほとんどありません)、アプリ自体の最良の値が参考になります。その旨を伝えてください。
選んだショットに対してのみ、60fps_get_motion_code を呼び出します。source が generated の場合にのみ使用し、generated フィールド自体の行を確認してください。withAnimation と .animation の値、スプリングのパラメーター、遅延、触覚フィードバックの呼び出しを確認します。応答のトップレベルにある animation、recipes、stagger_delay フィールドは無視してください。これらは汎用的な初期値で、多くの場合、どのショットでも同じです。recipes の結果にはショット番号が一切なく、タイミングが計算されるデモにもショット番号はありません。その場合はスキップして別のものを選んでください。ファイルは単独で実行できるデモなので、ループと再生もスキップしてください。
このスキルでは、ツール自身の指示に反して、コードファイルをユーザーに見せないでください。各ファイルから必要なのはタイミングを示す数行だけで、ファイル自体は長いためです。数値は実際のアプリの動画をAIが再現したものであり、実測値ではありませんが、非常に有用な参考情報です。呼び出し回数はユーザーの1日の利用上限に含まれるため、5回で止めてください。
5. システムを提案する
小さくまとめます。誰も覚えられないシステムは、システムとはいえません。
- アニメーション:用途ごとに名前を付けた値を約4つ用意します。たとえば、
Motion.feedback、Motion.change、Motion.arrival、Motion.emphasisと、ステップ2で意図的に例外としたものです。 利用できる最も新しいスプリングAPIを使います。iOS 17以降では、.spring(duration:bounce:)、またはduration:を指定した.smooth、.snappy、.bouncyを優先します。extraBounce:はプリセット 独自のバウンスに加算されます(.snappyにはすでにバウンスがあります)。そのため、変換後の値には.spring(duration:bounce:)を使います。.spring(response:dampingFraction:)を変換する場合、durationは responseと同じ値にします。damping fractionが1以下ならbounceは1からdamping fractionを引いた値、 1より大きいなら1をdamping fractionで割ってから1を引いた値です。blendDurationは削除します。 - スタガー:順番に表示するリストでは、項目ごとに1ステップずらします。最初の数項目(約8項目)だけを スタガー表示し、残りはまとめて表示します。これにより、長いリストでも待たせず、各ステップを読み取れる 長さに保てます。
- 触覚フィードバック:選択、軽いタップ、主要な操作または破壊的な操作に対する強いタップ、成功、エラーに それぞれ役割を用意します。ただし、アプリに呼び出し箇所がある役割、または提案によって呼び出し箇所を追加する 役割に限ります。成功は実際の達成にのみ使い、定期的な更新や同期には使いません。イベントごとに触覚 フィードバックは1回だけにし、リストの項目ごとには使いません。
- 視差効果を減らす:アプリ全体で使うルールを1つ用意します。ビューでは
@Environment(\.accessibilityReduceMotion)を読み取ります。モデルや環境にアクセスできないその他のコードでは、 メインアクター上でUIAccessibility.isReduceMotionEnabledを読み取ります。トランジションは.opacityに します。アニメーションだけ差し替えても動きは止まりません。状態に応じたscaleEffectやoffsetは、 速度が上がるだけで拡大・縮小や移動を続けるため、値を静止状態に保ちます。レイアウト変更(行の挿入、 リストの伸長など)では、隣接する要素が必ず動きます。その場合は、短い.easeInOut(約0.2秒)を使うのが 実用的なルールです。意味のある変化は引き続き変化させますが、移動はさせません。nilを返すヘルパーは 即時変更になり、フェードにはなりません。画面全体の移動など、即時変更を意図的に選ぶ場合にのみnilを 使い、クロスフェードするルールの場合は短い.easeInOutを使います。
名前、値、用途、置き換える現在の値、値の出所(スクリーンショットまたはアプリ独自のコード)を含む表に まとめます。その後、変更するすべての呼び出し箇所をファイルごとにまとめ、変更前と変更後の値、およびそれに 伴って変更が必要なすべてのスリープを列挙します。変更に着手する前に確認を取ります。
6. ファイルを書く
ユーザーが同意したら、アプリに既存のモーション用ファイルまたは型があれば(ステップ2を参照)、そこに値を
記述します。なければ、新しい Motion.swift を作成し、それぞれの用途を説明する短いコメント、触覚フィードバックの
役割(アプリにラッパーがあればそれを使い、なければ sensoryFeedback を使います)、および視差効果を減らす
ルールを適用する小さなヘルパーを追加します。引き渡す前にコンパイルします。可能ならアプリをビルドし、
難しければステップ1の最低対応バージョンを指定してファイルを型チェックします。その際、使用するアプリの型が
あれば、小さなスタブファイルを追加します。
xcrun --sdk iphonesimulator swiftc -typecheck -target arm64-apple-ios<floor>-simulator \
-swift-version <mode> Motion.swift Stub.swift
プロジェクトで分離性と並行処理のフラグを設定している場合は、それらも追加します。新しすぎるAPIを検出するのは
-target の最低対応バージョンです。その後、ユーザーから依頼があった場合にのみ、ファイルごとに少量ずつ
呼び出し箇所を移行し、関連するスリープをそれぞれアニメーションの完了に合わせて移動します。
簡潔に書きます。短い文と一般的な言葉を使います。emダッシュやenダッシュは使いません。
対象外
Android、Flutter、React Native、ウェブには対応しません。単一のアニメーションの場合は motion-reference スキルを
使います。アプリでアニメーションを使うべき箇所を探す場合は、まず find-animation-opportunities を使います。