ios-taste について
ios-tasteは、コードを書く前にユーザーのニーズを把握するSwiftUI設計スキルです。プロダクト思考、画面構成、パレット生成スクリプトを組み合わせ、設計の手順を示します。
主な機能
- 01ユーザーの状況と目標の分析
- 020.5秒で捉える画面形状の確認
- 03SwiftUIコンポーネント選択の指針
- 04コントラストを検証するパレット生成
- 05視覚的階層とデータ表示の指針
活用シーン
- 01ユーザーの直近の目標に沿ったSwiftUI画面の計画
- 02設定画面、ダッシュボード、コンテンツ画面の部品選定
- 03ライト・ダーク両モードの調和した配色作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ pproenca/ dot-skills --skill ios-tasteインストール後、Claude Code で/ios-tasteを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
AGENTS.mdmetadata.jsonreferences/apple-design-dna.mdscripts/generate_palette.pyverify_palette.py詳細
iOS Taste
Taste はピクセル単位から始まるものではありません。「この人は誰で、何を必要としているのか?」から始まります。ビジュアルの洗練は最後のステップです。最初のステップは、インターフェースのデザインが必然に感じられるほど、ユーザーの世界を深く理解することです。ほかの方法ではデザインできなかったと思えるほどに。
デフォルトのモードでは、レイアウトに直行してしまいます。技術的には正しいものの、実在する人のニーズを土台にしていないため、ありきたりに見える SwiftUI を生成します。このスキルでは、作業の順序を変えます。まずデザイナーのように考え、それからコードを書きます。
Phase 0: 0.5秒テスト(何よりも先に方向性を定める)
何かをデザインする前に、次の質問に一つだけ答えてください。
ユーザーが最初の0.5秒で目にするものは何ですか?——一言も読む前に
これはコンテンツの話ではありません。画面の形についてです。目を閉じて、画面を思い浮かべてください。何が目を引きますか?
- 画面の高さの40%を占めるリング? → フィットネスダッシュボード
- 大胆な白いテキストが載ったグラデーションカード? → 音楽/旅行/コンテンツ
- 空間に浮かぶ大きな数字? → 金融/健康の指標
- サムネイルのグリッド? → 写真/レシピ/ショッピングのコレクション
- 余白をたっぷり取ったすっきりしたフォーム? → 設定/プロフィール
答えが「テキストが並んだ行のリスト」なら、そこで止めてください。それはアプリではなく、スプレッドシートです。戻って、ビジュアルの形を見つけてください。
0.5秒の答えを、エクスペリエンスブリーフの最初の行に書いてください。
// 0.5s: Four warm gradient cards stacked on black — a cookbook
この一文が、その後のあらゆる判断の基盤になります。書いたコードがその形を実現していないなら、何かが間違っています。
Phase 1: デザイン思考(SwiftUI に触れる前に)
コードを一行でも書く前に、次の質問に答えてください。回答はコメントとして、または考えをまとめる中で書き留めてください。このフェーズを飛ばすと、ほかの AI 生成 UI と変わらない、正しいけれど魂のない出力になります。
1. ユーザーは誰ですか?
「フィットネス好き」ではなく、具体的な状況にいる実在の人物です。
- この画面を開くとき、どんな状況にいますか?(会議の合間に急いでいる?ソファでくつろいでいる?ワークアウトの途中?)
- ここにたどり着く前に何をしていましたか?(ランニングを終えた?リストを見ていた?通知を受け取った?)
- 10秒以内に何を達成したいですか?
これがあらゆることを形作ります。ワークアウト中のユーザーには、大きなタップ領域と一目で分かるデータが必要です。自宅でレシピを探しているユーザーは、詳しい情報と新しい発見を求めています。設定を行うユーザーは、必要な一つの切り替え項目を見つけて、その場を離れたいと思っています。
2. ユーザーにどんな気持ちになってほしいですか?
情報を表示するだけのアプリと、デザインされたアプリを分ける質問です。Apple Fitness はデータを見せるのではなく、体を動かす意欲を高めます。あらゆるデザイン上の選択が、その感情的な目標に役立ちます。
コンポーネントを選ぶ前に、どんな感情を促したいか決めてください。
- 意欲が高まる → 大胆な色、進捗の可視化、達成を祝う演出、大きな成果の数字
- 落ち着く/集中できる → 控えめな色調、ゆとりのある余白、さりげない動き
- 効率的 → コンパクトなレイアウト、明確な階層、最小限の装飾
- 楽しい驚きがある → 意外性のあるアニメーション、豊かなマテリアル、遊び心のある演出(達成バッジ、紙吹雪、3Dアイコン)
- 自信が持てる → 分かりやすいデータ表示、信頼感のある色(青/緑)、プロフェッショナルなタイポグラフィ
感情的な意図は、その後のあらゆるビジュアル上の判断を左右します。カラーパレット、スケール、余白、データをリストで表示するか視覚化するか、画面を密度の高い印象にするか、ゆったりした印象にするか、といった判断です。
3. ユーザーの目標と課題は何ですか?
画面ごとに、次の項目を特定してください。
- 主な目標 — ほとんどのユーザーがここで行う、最も重要な一つのこと
- 副次的な目標 — 一部のユーザーがときどき必要とすること
- 課題 — この領域でユーザーが不満に感じること
フィットネスの設定画面での主な目標は、「すべての設定を見る」ではありません。「ずっと気になっていた一つの項目を変更する」ことです。たとえば、週間目標が低すぎる、通知のタイミングが合わない、といったことです。課題は、必要な項目を見つけるために30個もの選択肢をかき分けなければならないことです。
3. それらの目標に役立つ機能は何ですか?
目標と機能を結び付けます。「この画面にどんな機能を追加できるか?」ではなく、「主な目標を簡単に達成するために必要な最小限の機能は何か?」と考えます。目標に役立たない機能はすべて余計なものです。
機能を優先度ごとに分類します。
- 必須 — これがないと主な目標を達成できない
- あるとよい — エクスペリエンスを大きく向上させる
- あってもよい — あると便利ですが、なくてもユーザーは困らない
4. 機能をどのように画面に落とし込みますか?
これは情報アーキテクチャです。何をどこに配置するかを決めます。
- 画面ごとに主なアクションを1つにする。 1つの画面で2つのことをさせる場合は、2つの画面に分けるか、プログレッシブディスクロージャを使います。
- データの種類ではなく、ユーザーの意図に基づいてグループ化する。 ユーザーは「通知設定を見たい」とは考えません。「ワークアウト中にスマートフォンが鳴らないようにしたい」と考えます。機能は技術的なカテゴリではなく、解決する問題に基づいてグループ化します。
- ナビゲーションはユーザーのメンタルモデルに沿わせる。 「設定」→「プロフィール」は分かりやすい構成です。一方、「設定」→「ヘルスケア連携」→「Apple Health」→「データのアクセス許可」は、ユーザーが一度だけ設定する項目にしては階層が深すぎます。専用の画面が必要か、画面内に直接表示できるかを検討します。
5. 各機能に役立つコンポーネントは何ですか?
ここで初めてSwiftUIについて考えます。ただし、ユーザーの意図を中心に考えます。
- ToggleとPicker — 選択肢が2つならToggleを使います。3つ以上の選択肢がある場合はPickerを使います。選択肢に説明が必要なら、選択画面へのナビゲーションリンクを使います。
- StepperとSlider — 正確な数値を指定する場合(レップ数を1、2、3と設定する場合など)はStepperを使います。正確な値より範囲が重要な場合(明るさ、音量、週あたりの目標時間など)はSliderを使います。
- インライン表示とプッシュナビゲーション — 詳細が1〜2行ならインラインで表示します。独立したコンテキストにする価値があるほど詳細な場合は、新しい画面へ遷移させます。
- シートとプッシュ — シートは独立して完結するタスク(作成、プロフィール編集、フィルタリング)に使います。階層構造のコンテンツを掘り下げる場合はプッシュを使います。
- ListとScrollView — 同種の項目を集めた一覧(連絡先、設定、メッセージ)にはListを使います。異なる要素を組み合わせたレイアウト(ヒーロー画像、材料、手順を含むレシピの詳細など)にはScrollViewを使います。
コンポーネントの選択そのものがデザインです。週あたりのワークアウト目標にSliderを使うと、試行錯誤できて気軽な印象になります。同じ値にStepperを使うと、正確で事務的な印象になります。どちらも間違いではありません。適切な選択は、ユーザーがどのような人で、どんな状況にいるかによって決まります。
フェーズ2:ビジュアルデザイン
フェーズ1を終えると、ユーザーが誰で、何を必要とし、どのように感じるべきで、どのコンポーネントがそのニーズに役立つかが分かります。次は、見た目を美しく仕上げます。フェーズ1で定めた感情面の意図が、ここでのあらゆる選択を導きます。
1. サイズによる階層化
フォントの太さだけではなく、サイズに大きな差をつけます。画面上で最も重要なものは、太字にするだけでなく、物理的に大きく表示します。
- ヒーローとなる数値はディスプレイサイズで表示する — カロリー数、歩数、価格などを画面上で目立たせます。
.system(size: 64)または.fontDesign(.rounded)を指定した.largeTitleを使います。Apple Fitnessでは「120」が画面の40%を占めています。重要なデータを縮小してリストの一項目に押し込めないでください。 - 補足テキストは控えめにする — ヒーロー要素以外はすべて、
.secondaryを指定した.captionまたは.footnoteにします。ヒーロー要素と補足要素のコントラストが、階層を生み出します。 - 余白はゆとりを伝える — 空白を残します。黒や白の広がりの中に浮かぶ数値は、密集したリストに詰め込まれた同じ数値よりも強い印象を与えます。余白は重要性を伝えます。
2. 色は感覚ではなく、数学です
色を手作業で選んではいけません。色の調和は、数学的に解決済みの問題です。このスキルには、1つのシード色相からすべての色を計算するパレットジェネレーターが含まれています。類似色の調和、WCAG コントラスト検証、ライトモードとダークモードのバリエーションに対応しています。
ビューのコードを書く前に、必ずパレットジェネレーターを実行してください。
python scripts/generate_palette.py \
--seed <hue-degrees> \
--mode both \
--items <collection-count> \
--app "App Name"
シード色相の目安:
- 0–30° = 暖色(料理、ソーシャル、デート)
- 30–60° = ゴールド系(金融、生産性)
- 60–150° = 緑(健康、フィットネス、自然)
- 150–210° = シアン/ティール(テクノロジー、コミュニケーション)
- 210–270° = 青(信頼、ビジネス、天気)
- 270–330° = 紫(クリエイティブ、音楽、高級感)
- 330–360° = ピンク/赤(エネルギー、食べ物)
生成された enum Palette { ... } を Swift ファイルの先頭に含め、その色だけを使用してください。パレットは計算によって生成されます。すべての色はシードと数学的に関連し、コントラスト比は事前に検証済みで、ライトモードとダークモードのバリエーションも含まれています。
必ず守るルール:
- アプリごとにシード色相は1つだけです。 すべての色はそこから派生します。
- コレクションには類似色のバリエーション(
--itemsフラグ)を使い、 ランダムな色相は使いません。シードから±30°の範囲にあるため、色同士が調和します。 - パレットの色として
Color.red、.green、.blueを使ってはいけません。 これらはステータス表示用のシステムカラーです。 - ビューのコード内に
Color(hue:)を場当たり的に散在させるのではなく、Palette.primary、Palette.cardBackgroundなどを使用してください。
3. データは羅列せず、見せましょう
データがコンテンツそのものである場合(フィットネスの指標、財務統計、進捗など)は、ラベルに入れるのではなく、視覚化してください。
- 目標に対する進捗にはリングやゲージ
- 時間の経過による傾向にはスパークライン
- 単位ラベルを小さな大文字で添えた大きな数値
- 内訳(主要栄養素、時間配分)には色分けしたバー
LabeledContent("Steps", value: "8,432") は情報です。一方、.title の大きな「8,432」の下にスパークラインを添えれば、体験になります。どちらを使うかは、フェーズ1で定めた感情的な意図によって決まります。
4. カードベースの構成
何にでも .insetGrouped の List を使うのはやめましょう。内容が多様な場合は、角丸の長方形コンテナーで構成してください。
- ダーク背景では、
RoundedRectangle(cornerRadius: 16)に.fill(.secondary.opacity(0.15))を適用したカード - 各カードは、独自の階層構造を持つ独立した視覚ユニットです
- 視覚的な豊かさを出すため、カードにグラデーション背景を使うこともできます(Apple Fitness+ Plans のカードのように)
- カードベースのレイアウトには、
ScrollView内でLazyVGridまたはLazyVStackを使います
リストは、同種の行(連絡先、メッセージ、設定)に適しています。カードは、ダッシュボード、概要、情報量の多い画面に適しています。
5. コンテンツのリアリティ
データそのものがデザインです。すべてのプレビューで、筋の通ったストーリーを伝えましょう。
- 実在感のある名前(「Elena Marsh」)、もっともらしい数値(「$47.83」、「4.3」)、長さに変化をつけたデータ、時間的なリアリティ(「2時間前」、「昨日」)
- 関連性のあるデータ(デザイナー → デザイン部門)
- プレビューのデータが偽物に見えるなら、デザインも偽物に見えます
6. 控えめにする
何を省くかが、センスを形作ります。説明用のヘッダーは不要です。アイコンを均一にそろえる必要もありません。チュートリアルのオーバーレイも、デモ用の命名も不要です。要素ごとに「これを取り除くとどうなるか?」と問いかけてください。何も変わらないなら、取り除きましょう。
4. クラフト
自然な使い心地を生む、目に見えない細部:
- 変化する数値には
.monospacedDigit() - 独自のサイズには
@ScaledMetric - カウンターには
.contentTransition(.numericText()) - 意味のある状態変化には
.sensoryFeedback()(触覚フィードバックの乱用は避けます) - キーと値の組み合わせには
LabeledContent - アクセシビリティは、コンプライアンスではなくデザインとして考えます
5. 個性
画面ごとに独自の個性を持たせます。個性は次の要素から生まれます。
- 分野に適したコンテナーとカラーパレット
- コンテンツに合わせたタイポグラフィとインタラクションパターン
- ナビゲーションバーを隠しても、どのアプリか分かりますか?
両方のフェーズを適用する
SwiftUI のビューを作成するよう求められた場合:
-
フェーズ 1 — ユーザー、その目標、機能のグループ分け、画面構成、コンポーネントの選択について検討します。デザインの意図を示す簡潔なメモを、コードコメントまたは回答内に記述します。これは必須です。設計された体験と、装飾されたレイアウトを分けるものです。
-
フェーズ 2 — 5 つの基本原則をすべて適用した SwiftUI コードを記述します。現実的なデータモデルとプレビュー用コンテンツから始めます。まずは最小限に構築し、必要なものだけを追加してから、細部まで丁寧に仕上げます。
-
セルフチェック — 仕上げる前に、次のように問いかけます。「フェーズ 1 で特定した状況でこのアプリを使う実際のユーザーは、この画面が自分のために設計されたと感じるでしょうか?」そうでなければ、フェーズ 1 のどこかが間違っています。戻って見直してください。
「センスがない」とは
// NO TASTE — jumped straight to layout, no user thinking
struct DemoView: View {
var body: some View {
NavigationStack {
List {
Section("Instructions") {
Text("This demo shows how lists work")
}
Section("Items") {
ForEach(1...5, id: \.self) { i in
HStack {
Image(systemName: "star")
Text("Item \(i)")
Spacer()
Text("Detail")
.foregroundStyle(.secondary)
}
}
}
}
.navigationTitle("Demo")
}
}
}
ユーザーへの配慮がありません。目標もありません。説明用のヘッダーがあります。アイコンはすべて同じです。番号付きのプレースホルダーがあります。名前はありきたりで、個性がありません。
「センスがある」とは
// GOLDEN — Weather-inspired fitness dashboard
// User: Alex, 28, just finished a morning run, wants to see today's stats
// Emotional intent: MOTIVATED — celebrate the effort, inspire tomorrow
// Hero: calorie ring dominating the top half
struct FitnessCardView: View {
let calories: Int = 847
let goal: Int = 1000
var body: some View {
ScrollView {
VStack(spacing: 20) {
// Hero ring — 40% of visible screen, not a row in a list
ZStack {
Circle()
.stroke(.quaternary, lineWidth: 20)
Circle()
.trim(from: 0, to: Double(calories) / Double(goal))
.stroke(calorieGradient, style: StrokeStyle(lineWidth: 20, lineCap: .round))
.rotationEffect(.degrees(-90))
VStack(spacing: 4) {
Text("\(calories)")
.font(.system(size: 56, weight: .bold, design: .rounded))
Text("of \(goal) cal")
.font(.subheadline)
.foregroundStyle(.secondary)
}
}
.frame(width: 220, height: 220)
.padding(.top, 20)
// Stat cards — NOT LabeledContent rows
HStack(spacing: 12) {
statCard("Distance", value: "5.2 km", color: .blue)
statCard("Time", value: "28:14", color: .green)
statCard("Pace", value: "5'26\"", color: .orange)
}
}
.padding()
}
}
private func statCard(_ label: String, value: String, color: Color) -> some View {
VStack(spacing: 6) {
Text(value)
.font(.system(.title3, design: .rounded))
.fontWeight(.semibold)
Text(label)
.font(.caption)
.foregroundStyle(.secondary)
}
.frame(maxWidth: .infinity)
.padding(.vertical, 16)
.background(color.opacity(0.1), in: .rect(cornerRadius: 12))
}
private var calorieGradient: AngularGradient {
AngularGradient(colors: [.red, .orange, .yellow], center: .center)
}
}
デザインコメントで、ユーザーが置かれている状況と感情面での意図を説明しています。ヒーローリングが画面の主役です(List 行内の ProgressView ではありません)。統計カードには LabeledContent ではなく、色付きの背景を使っています。タイトルを読まなくても、フィットネスアプリだとわかります。リングそのものがアイデンティティです。
コンポーネントの選択肢:クイックリファレンス
チュートリアルでよく使われるコンポーネントを反射的に選びそうになったら、いったん止まってください。
| 使わない(反射的に選びがち) | おすすめ(代わりに選ぶ) |
|---|---|
List + ForEach | カードを使った ScrollView + LazyVStack |
Form + Section | ScrollView + GroupBox(.regularMaterial) |
指標に LabeledContent | ヒーロータイポグラフィ .system(size:design:.rounded) |
ProgressView | Circle().trim(from:to:) または Canvas |
Button("Label") | .borderedProminent + .controlSize(.large) |
Section 内の Toggle | セグメントコントロールまたはカスタムピル |
テキストに .system(size:) | .largeTitle / .title + .fontDesign(.rounded) |
List のデフォルト背景 | グラデーション、.regularMaterial、色付きコンテナ |
活用したい iOS 18 以降のモダンな API:MeshGradient、.scrollTransition、.containerRelativeFrame、.visualEffect、Canvas、TimelineView、.scrollTargetBehavior(.paging)、UnevenRoundedRectangle。
Apple の参考例:天気(グラデーションカード)、株価(ヒーローチャート)、ヘルスケア(色付きのリング)、フィットネス(アクティビティカード)、ジャーナル(写真カード)、連絡先(グラデーションのポスター、ガラス風のアバター、エンティティごとの色のアイデンティティ)。
画面そのものをコンテンツにする
iOS の連絡先アプリを研究してください。詳細画面は、連絡先のデータを入力したフォームではありません。画面全体が連絡先そのものです。人物のアバターの色に合わせたフルブリードのグラデーション、ガラス風の枠で囲まれたモノグラム、特大の太字で表示された名前。これはレコードではなく、ポスターです。
これはセンスの最高峰です。UI がコンテンツに溶け込みます。画面はデータを囲むのではなく、データそのものになります。
実現するテクニック:
- エンティティごとのグラデーション — 連絡先、プレイリスト、レシピのそれぞれに、独自の色のアイデンティティを持たせます。エンティティのアクセントカラーから
MeshGradientまたはLinearGradientを作成します。.ignoresSafeArea()を使い、背景をナビゲーションバーの背後まで広げます。 - ガラスとマテリアルのレイヤー —
.stroke(.ultraThinMaterial)の枠を付けたアバターの円。.ultraThinMaterialの円形にしたアクションボタン。グラデーションが透けて見える.regularMaterialを使ったカード。影を使わずに奥行きを生み出します。 - リスト内のスマートなタイポグラフィ — Apple の連絡先アプリでは、姓を太字にし、名は通常のウェイトのままにしています。この小さな工夫で、アルファベット順の検索が大幅に速くなります。自分の領域でも同等のタイポグラフィ階層を見つけてください。
- 美しさを保つ編集モード — 連絡先アプリの編集フォームでも、暗いカード、色付きのアクションボタン(赤のマイナス、緑のプラス)、同じアバターのヒーローを使っています。編集モードをありきたりなフォームに劣化させてはいけません。閲覧モードのビジュアルランゲージを保ちます。
参考資料:Apple Design DNA
具体的なデザインを決める際は、このスキルのディレクトリにある references/apple-design-dna.md
をお読みください。iOS シミュレーターで Apple Fitness と Apple Contacts を体系的に
調査し、各画面について実際のアクセシビリティツリー、実測値、デザイン分析から抽出したパターンを掲載しています。
確認する主なセクション:
- ダッシュボードモードとユーティリティモード — ダークカードと明るいリストを使い分ける場面
- 共通の寸法 — カードの角丸(16pt)、パディング(18pt)、間隔(10pt)
- オンボーディングのテンプレート — 機能一覧パターンとヒーローイラストパターン
- ボタンの階層 — 塗りつぶしのプライマリ、アウトラインのセカンダリ、配置に基づく破壊的アクション
- グラデーション上のガラス表現 — 詳細画面を上質に見せる Contacts のポスターテクニック
- 空の状態 — 「データがありません」というメッセージではなく、視覚化のスケルトンを表示する
心構え
デザインもできる開発者ではありません。人を第一に考え、その成果を SwiftUI で表現するデザイナーです。 コードは表現の手段です。プロダクトとは、人がスマートフォンを手に取り、まるで自分のためだけに作られたかのようなインターフェースに心を動かされる、その瞬間です。