Apple HIG デザイナー は何をしますか?
Appleのヒューマンインターフェースガイドライン(HIG)に従って、美しくネイティブなiOSアプリをデザインします。ネイティブコンポーネント、適切なタイポグラフィ、セマンティックカラー、Appleのデザイン原則を使用して、アクセシブルで直感的なインターフェースを作成します。
このスキルの機能
ネイティブでAppleのガイドラインに従ったiOSアプリのデザインと構築を支援します。
- iOSコンポーネントの生成 - SwiftUIおよびUIKitコンポーネントを作成
- デザインの検証 - Apple HIGへの準拠をチェック
- アクセシビリティの確保 - VoiceOver、Dynamic Type、カラーコントラスト
- デザイン原則の適用 - 明確さ、従属、深み
- セマンティックカラーの使用 - 自動ダークモード対応
- タイポグラフィの実装 - San Franciscoフォントシステム
- スペーシングの遵守 - 8ポイントグリッドシステムとセーフエリア
Appleのデザイン原則
1. 明確さ
コンテンツを明確かつ焦点を絞ったものにします。
テキストはあらゆるサイズで読みやすく、アイコンは正確で明晰、装飾は控えめで適切であり、機能性に焦点を当てたデザインが機能を推進します。
// ✅ 明確で焦点を絞ったコンテンツ
Text("おかえりなさい、Sarah")
.font(.title)
.foregroundColor(.primary)
// ❌ 不明瞭で散らかった
Text("おかえりなさい、Sarah!!!")
.font(.title)
.foregroundColor(.red)
.background(.yellow)
.overlay(Image(systemName: "star.fill"))
2. 従属
UIはコンテンツの理解と操作を助けますが、競合することはありません。
インターフェースはコンテンツに従属し、軽量なビジュアル表現を使用してコンテンツに焦点を当て、コンテンツに余白を与えます。
// ✅ コンテンツ重視
VStack(alignment: .leading, spacing: 8) {
Text("記事タイトル")
.font(.headline)
Text("ここに記事の内容が入ります...")
.font(.body)
.foregroundColor(.secondary)
}
.padding()
// ❌ 邪魔なUI
VStack(spacing: 8) {
Text("記事タイトル")
.font(.headline)
.foregroundColor(.white)
.background(.blue)
.border(.red, width: 3)
}
3. 深み
視覚的なレイヤーと現実的な動きは階層を伝え、人々が関係性を理解するのに役立ちます。
明確な視覚レイヤーと現実的な動きは、活気を与え、理解を促進します。タッチと発見しやすさは喜びを高め、コンテキストを失うことなく機能にアクセスできるようにします。
// ✅ 明確な階層構造
ZStack {
Color(.systemBackground)
VStack {
// 浮き上がり効果のあるカード
CardView()
.shadow(radius: 8)
}
}
// ぼかしを使用した深みの表現
Text("コンテンツ")
.background(.ultraThinMaterial)
iOS UIコンポーネント
ナビゲーションパターン
1. ナビゲーションバー
ナビゲーションと操作のためのトップバー。
NavigationStack {
List {
Text("アイテム 1")
Text("アイテム 2")
}
.navigationTitle("タイトル")
.navigationBarTitleDisplayMode(.large)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button("追加") {
// アクション
}
}
}
}
ガイドライン:
- トップレベルのビューには大きいタイトルを使用
- 詳細ビューにはインラインタイトルを使用
- アクションは現在のコンテキストに関連するものにする
- ツールバーアイテムは最大2~3つ
2. タブバー
トップレベルの遷移先のためのボトムナビゲーション。
TabView {
HomeView()
.tabItem {
Label("ホーム", systemImage: "house")
}
SearchView()
.tabItem {
Label("検索", systemImage: "magnifyingglass")
}
ProfileView()
.tabItem {
Label("プロフィール", systemImage: "person")
}
}
ガイドライン:
- タブは最大3~5つ
- アイコンにはSF Symbolsを使用
- ラベルは簡潔に(1単語)
- タブを非表示または無効にしない
- 同じビューでタブバーとツールバーを併用しない
3. リスト
スクロール可能なアイテムのリスト。
List {
Section("今日") {
ForEach(items) { item in
NavigationLink {
DetailView(item: item)
} label: {
HStack {
Image(systemName: item.icon)
.foregroundColor(.accentColor)
Text(item.title)
}
}
}
}
}
.listStyle(.insetGrouped)
リストスタイル:
.plain- エッジツーエッジの行.insetGrouped- 角丸でインセットされたセクション(iOSのデフォルト).sidebar- ナビゲーションサイドバー用
4. シート(モーダル)
コンテンツをモーダルで表示します。
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Button("詳細を表示") {
showSheet = true
}
.sheet(isPresented: $showSheet) {
DetailView()
.presentationDetents([.medium, .large])
}
}
}
シートのディテント:
.medium- 画面半分.large- 全画面- カスタム高さも可能
フォームコントロール
1. ボタン
主なアクションコントロール。
// 塗りつぶしボタン(プライマリアクション)
Button("続行") {
// アクション
}
.buttonStyle(.borderedProminent)
// 枠線ボタン(セカンダリアクション)
Button("キャンセル") {
// アクション
}
.buttonStyle(.bordered)
// プレーンボタン(ターシャリアクション)
Button("もっと詳しく") {
// アクション
}
.buttonStyle(.plain)
ボタンの階層:
- プロミネント - プライマリアクション(画面に1つ)
- 枠線 - セカンダリアクション
- プレーン - ターシャリアクション、リンク
ガイドライン:
- 最小タップターゲット: 44x44ポイント
- ボタンのラベルには動詞を使用
- 破壊的なアクションには確認を求める
2. テキストフィールド
テキスト入力コントロール。
@State private var username = ""
@State private var password = ""
VStack(alignment: .leading, spacing: 16) {
// 標準テキストフィールド
TextField("ユーザー名", text: $username)
.textFieldStyle(.roundedBorder)
.textContentType(.username)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
// セキュアフィールド
SecureField("パスワード", text: $password)
.textFieldStyle(.roundedBorder)
.textContentType(.password)
}
テキストコンテンツタイプ:
.username- ユーザー名フィールド.password- パスワードフィールド.emailAddress- メールアドレスフィールド.telephoneNumber- 電話番号.creditCardNumber- クレジットカード
3. トグル
ブール値コントロール(スイッチ)。
@State private var isEnabled = false
Toggle("通知を有効にする", isOn: $isEnabled)
.toggleStyle(.switch)
ガイドライン:
- ラベルはトグルが制御するものを説明
- 効果は即時であるべき
- バイナリ選択にのみ使用
4. ピッカー
選択コントロール。
@State private var selectedSize = "M"
let sizes = ["S", "M", "L"]
// メニュースタイル
Picker("サイズ", selection: $selectedSize) {
ForEach(sizes, id: \.self) { size in
Text(size).tag(size)
}
}
.pickerStyle(.menu)
// セグメントスタイル(2~5個のオプション用)
Picker("サイズ", selection: $selectedSize) {
ForEach(sizes, id: \.self) { size in
Text(size).tag(size)
}
}
.pickerStyle(.segmented)
ピッカースタイル:
.menu- ドロップダウンメニュー(デフォルト).segmented- セグメントコントロール(2~5個のオプション).wheel- スクロール可能なホイール.inline- インラインリスト(フォーム内)
カードとコンテナ
カードビュー
struct CardView: View {
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("タイトル")
.font(.headline)
Text("コンテンツの詳細をここに説明します。")
.font(.subheadline)
.foregroundColor(.secondary)
.lineLimit(2)
Spacer()
Button("アクション") {
// アクション
}
.buttonStyle(.borderedProminent)
}
.padding()
.frame(width: 300, height: 200)
.background(Color(.systemBackground))
.cornerRadius(12)
.shadow(color: .black.opacity(0.1), radius: 8, x: 0, y: 4)
}
}
タイポグラフィ
San Franciscoフォントシステム
最適な読みやすさのために設計されたAppleのシステムフォント。
// Dynamic Typeテキストスタイル
Text("Large Title").font(.largeTitle) // 34pt
Text("Title").font(.title) // 28pt
Text("Title 2").font(.title2) // 22pt
Text("Title 3").font(.title3) // 20pt
Text("Headline").font(.headline) // 17pt semibold
Text("Body").font(.body) // 17pt regular
Text("Callout").font(.callout) // 16pt
Text("Subheadline").font(.subheadline) // 15pt
Text("Footnote").font(.footnote) // 13pt
Text("Caption").font(.caption) // 12pt
Text("Caption 2").font(.caption2) // 11pt
Dynamic Type対応のカスタムフォント
// Dynamic Typeで拡大縮小するカスタムフォント
Text("カスタムテキスト")
.font(.custom("YourFont-Regular", size: 17, relativeTo: .body))
フォントウェイト
Text("Light").fontWeight(.light)
Text("Regular").fontWeight(.regular)
Text("Medium").fontWeight(.medium)
Text("Semibold").fontWeight(.semibold)
Text("Bold").fontWeight(.bold)
Text("Heavy").fontWeight(.heavy)
タイポグラフィガイドライン
推奨事項:
- ✅ 一貫性のためにシステムフォント(San Francisco)を使用
- ✅ アクセシビリティのためにDynamic Typeをサポート
- ✅ セマンティックテキストスタイル(.headline, .body など)を使用
- ✅ 本文テキストの最小サイズ: 17pt
- ✅ 行間: フォントサイズの120~145%
非推奨事項:
- ❌ フォントサイズを多用しない(システムスタイルに従う)
- ❌ テキストを11ptより小さくしない
- ❌ 長いテキストにすべて大文字を使用しない
- ❌ Dynamic Typeを無効にしない
カラー
セマンティックカラー
ライト/ダークモードに自動的に適応するカラー。
// UI要素のカラー
Color(.label) // プライマリテキスト
Color(.secondaryLabel) // セカンダリテキスト
Color(.tertiaryLabel) // ターシャリテキスト
Color(.quaternaryLabel) // 透かしテキスト
Color(.systemBackground) // プライマリ背景
Color(.secondarySystemBackground) // セカンダリ背景
Color(.tertiarySystemBackground) // ターシャリ背景
Color(.systemFill) // 塗りつぶしカラー
Color(.secondarySystemFill)
Color(.tertiarySystemFill)
Color(.quaternarySystemFill)
Color(.separator) // 区切り線
Color(.opaqueSeparator) // 不透明な区切り線
システムカラー
// 標準システムカラー(ダークモードに適応)
Color(.systemRed)
Color(.systemOrange)
Color(.systemYellow)
Color(.systemGreen)
Color(.systemMint)
Color(.systemTeal)
Color(.systemCyan)
Color(.systemBlue)
Color(.systemIndigo)
Color(.systemPurple)
Color(.systemPink)
Color(.systemBrown)
Color(.systemGray)
ダークモード対応のカスタムカラー
// アダプティブカラーを定義
struct MyTheme {
static let customBackground = Color("CustomBackground")
}
// Assets.xcassetsで、以下のカラーセットを作成:
// - Any Appearance: #FFFFFF
// - Dark Appearance: #000000
カラーコントラストガイドライン
WCAG AA準拠:
- 標準テキスト: 少なくとも4.5:1のコントラスト比
- 大きいテキスト(24pt以上): 少なくとも3:1のコントラスト比
- UIコンポーネント: 3:1のコントラスト比
カスタムカラー:
- 「コントラストを上げる」が有効な状態でテスト
- 重要なテキストは7:1を目指す
- 両方のモードで十分なコントラストを提供
スペーシングとレイアウト
8ポイントグリッドシステム
すべてのスペーシングは8の倍数にします。
// スペーシング値
.padding(8) // 8pt
.padding(16) // 16pt(標準)
.padding(24) // 24pt
.padding(32) // 32pt
.padding(40) // 40pt
.padding(48) // 48pt
// エッジ固有のパディング
.padding(.horizontal, 16)
.padding(.vertical, 24)
.padding(.top, 16)
.padding(.bottom, 16)
セーフエリア
デバイスのセーフエリアを尊重します。
// セーフエリア内のコンテンツ(デフォルト)
VStack {
Text("コンテンツ")
}
// セーフエリアを超えて拡張
VStack {
Color.blue
}
.ignoresSafeArea()
// 上部のみ拡張
VStack {
Color.blue
}
.ignoresSafeArea(edges: .top)
タッチターゲット
最小インタラクティブサイズ: 44x44ポイント。
Button("タップ") {
// アクション
}
.frame(minWidth: 44, minHeight: 44)
スペーシングガイドライン
// コンポーネント間のスペーシング
VStack(spacing: 8) { // 密なスペーシング
Text("行 1")
Text("行 2")
}
VStack(spacing: 16) { // 標準のスペーシング
Text("セクション 1")
Text("セクション 2")
}
VStack(spacing: 24) { // 緩いスペーシング
SectionView()
SectionView()
}
アクセシビリティ
VoiceOverサポート
視覚障碍者および弱視ユーザーのためのスクリーンリーダー。
// アクセシブルなラベル
Image(systemName: "heart.fill")
.accessibilityLabel("お気に入り")
// アクセシブルな値
Slider(value: $volume)
.accessibilityLabel("ボリューム")
.accessibilityValue("\(Int(volume * 100))%")
// アクセシブルなヒント
Button("共有") {
share()
}
.accessibilityHint("このアイテムを他のユーザーと共有します")
// 要素のグループ化
HStack {
Image(systemName: "person")
Text("山田 太郎")
}
.accessibilityElement(children: .combine)
// VoiceOverから隠す
Image("decorative")
.accessibilityHidden(true)
Dynamic Type
ユーザーが選択したテキストサイズをサポートします。
// システムフォントで自動的にサポート
Text("このテキストは拡大縮小します")
.font(.body)
// 拡大縮小を制限する場合
Text("このテキストには制限があります")
.font(.body)
.dynamicTypeSize(...DynamicTypeSize.xxxLarge)
// Dynamic Type対応のカスタムフォント
Text("カスタムフォント")
.font(.custom("YourFont", size: 17, relativeTo: .body))
色覚障碍への配慮
色覚障碍ユーザーのためにデザインします。
// 色だけで判断させない
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text("成功")
}
// 色のみに依存しない
Circle()
.fill(.green)
// ❌ 色のみ
// 形状やアイコンと組み合わせる
HStack {
Image(systemName: "checkmark.circle.fill")
Circle().fill(.green)
}
// ✅ 色 + 形状
動きを減らす設定への対応
ユーザーのモーション設定を尊重します。
@Environment(\.accessibilityReduceMotion) var reduceMotion
var animation: Animation {
reduceMotion ? .none : .spring()
}
Button("アニメーション") {
withAnimation(animation) {
// アニメーション
}
}
コントラストを上げる設定への対応
ハイコントラストモードをサポートします。
...