WWDC Quick Look 💓 By SwiftGGTeam
優れたデザインの原則

優れたデザインの原則

元の動画を見る

ハイライト

Apple はデザインを「意図を持って創造すること」と定義し、Purpose、Agency、Responsibility、Familiarity、Flexibility、Simplicity、Craft、Delight という 8 つの原則を軸に、AI 時代に特に守るべきデザイン指針を示しました。すべての Feature はユーザーの時間、注意、信頼を消費します。「何を作らないか」を決めることは、「何を作るか」を決めること以上に重要です。

主要内容

Purpose:先に考え抜き、それから手を動かす

00:32

多くのチームは要件を受け取ると、まずプロトタイプを描いたりコードを書いたりしがちです。Apple はこの Session の冒頭で、そこに冷水を浴びせます。最初の線を引く前、最初のコードを書く前に、まず自問するべきです。このものには Purpose、つまり目的があるのか。

プロダクトに追加するすべての Feature は、ユーザーの時間、注意、信頼を求めます。この 3 つは高価で、無駄にできません。だからこそ「何をしないか」を決めることは、「何をするか」よりも判断力を問います。機能が存在するなら、「他社が持っているからうちにも必要」ではなく、明確な価値提案が必要です。

Agency:コントロールをユーザーに返す

01:52

Agency、つまりユーザーの主体性の中心は、ユーザーが自分のやり方で目的を達成できるようにすることです。インターフェイスはユーザーと目標の間に立ちはだかるべきではなく、あらかじめ決められた一本道へ誘導すべきでもありません。

しかし自由を与えると、ユーザーはミスをします。そこで Forgiveness、つまりやり直せる余地が必要です。ユーザーがファイルを誤って削除したり、間違ったメッセージを送ったりしたら、Undo を簡単にするべきです。本当に不可逆な破壊的操作の前にだけ、阻止型の確認ダイアログを使います。この設計なら、ユーザーはシステムが受け止めてくれると分かるため、安心して探索できます。

Responsibility:プライバシーと安全は最低ライン

03:38

Responsibility、つまり責任はプライバシーから始まります。Apple の立場は明確です。App 起動直後に権限リクエストを投げてはいけません。ユーザーがその App が何をするものか理解する前に、位置情報、写真、連絡先を求められる。これは見知らぬ人がいきなり電話番号を聞いてくるようなものです。信頼できるでしょうか。

責任あるインターフェイスは、正しいタイミングでデータを求め、必要なデータだけを求め、その用途を透明に説明します。

AI の文脈では、責任はさらに重要です。(05:10)Apple はレシピ App の例を挙げます。ユーザーがピーナッツアレルギーを登録しているのに、AI がピーナッツ入りのレシピを推薦したら、実際の身体的被害につながります。Apple の姿勢は、最悪のケースを予測し、安全ガードレールを設けることです。リスクが価値を上回るなら、「AI は不正確な場合があります」という免責文でユーザーに投げるのではなく、その機能を削除します。

Familiarity:ユーザーがすでに知っている認知を使う

06:02

Familiarity、つまりなじみやすさは、他人のデザインを丸写しすることではなく、ユーザーがすでに知っているものを使うことです。Metaphor は初期のグラフィカルインターフェイスから使われてきました。ゴミ箱は削除を表します。現実のゴミ箱が不要なものを入れる場所だとユーザーが知っているからです。

良い Metaphor は具体的すぎてもいけません。ユーザーが元のものを見たことがないかもしれないからです。抽象的すぎてもいけません。機能を推測できないからです。一貫性も重要です。同じ見た目のコントロールは同じ動作をするべきです。よく使う操作の位置は、画面やデバイスをまたいでも固定されるべきです。Mac の閉じるボタンは常に左上にあり、ユーザーは考えなくてもクリック先が分かります。

Flexibility:さまざまな利用シーンに適応する

08:54

同じ人が音楽を聴くときでも、自宅、ランニング中、運転中では操作方法がまったく違います。柔軟なインターフェイスは、こうした現実の状況に適応します。iPhone では素早いタッチ操作、Mac では深いワークフロー、CarPlay では完全なハンズフリー操作が求められます。

柔軟性はインクルーシブさにも表れます。ユーザーの年齢、言語、能力はさまざまです。初日から全員をカバーする必要はありませんが、体験をより包摂的にする方法を考え始める必要があります。たとえば、コントロールの配置をカスタマイズできるようにしたり、あまり使わないボタンを隠せるようにしたりします。

Simplicity:不要なものを取り除き、中心目的を際立たせる

11:13

Apple は Simplicity、つまり簡潔さと、Minimalism、つまり極簡を明確に分けています。すべての機能を 1 つのメニューに押し込めば、画面はきれいに見えるかもしれませんが、使うと複雑になります。本当の簡潔さは摩擦を取り除くことです。ユーザーが必要なものを苦労なく見つけられる状態です。

簡潔さは Concise と Clear で実現します。精緻なインターフェイスは平易な言葉を使い、専門用語を取り除き、操作ステップを減らします。明確なインターフェイスは Hierarchy で注意を導きます。順序、余白、コントラストによって、画面上で最も重要なものが一目で見えるようにします。

ときには、簡潔さは情報を増やすことを意味します。(13:06)シンプルな再生/一時停止ボタンでも、しばらく一時停止した後に戻ってきたユーザーには文脈が必要です。どこまで再生したのか。残り時間はどれくらいか。これらの情報を追加することで、ユーザーは情報に基づいて判断できます。それこそが簡潔さです。

Craft:細部が信頼を決める

13:42

Craft は、ユーザーが感じ取れる作り込みです。ボタンを押しても反応しない、スクロールが引っかかる、アイコンが揃っていない、画面回転でレイアウトが壊れる。こうした細部は、プロダクト全体の品質を疑わせます。

高品質な Craft には、デバイスをまたいでも美しく見えるタイポグラフィ、ライト/ダークモードに適応する配色、明確なアイコン、滑らかなアニメーションフィードバックが含まれます。これらは飾りではなく、信頼の土台です。Craft は反復から生まれ、継続的なプロセスです。プラットフォームが更新されたり新しいハードウェアが出たりしたら、自分たちの体験に価値があるかを積極的に評価する必要があります。

Delight:すべての原則が正しく働いた先に自然に生まれるもの

15:48

Delight、つまり喜びは定義しにくいものですが、体験すれば分かります。Apple の指摘は直接的です。紙吹雪やエフェクトで喜びを作ろうとしてはいけません。本当の Delight は、ユーザーにどんな感情を持ってほしいか、リラックス、自信、興奮などを明確にし、それをデザインの中で継続的に強化することから生まれます。Delight は、他のすべての原則が適切に機能した後の自然な結果です。

詳細

Undo で阻止型確認ダイアログを置き換える

Agency 原則において、Forgiveness の具体的な実装は強力な Undo サポートです。以下は SwiftUI で UndoManager と下部トーストを組み合わせる実装です。

struct TaskListView: View {
    @State private var tasks = ["週報を書く", "コードをレビュー", "ドキュメントを更新"]
    @Environment(\.undoManager) private var undoManager
    @State private var showUndoToast = false
    @State private var deletedTask: String?
    @State private var deletedIndex: Int?

    var body: some View {
        List {
            ForEach(tasks, id: \.self) { task in
                Text(task)
            }
            .onDelete { indexSet in
                let index = indexSet.first!
                deletedTask = tasks[index]
                deletedIndex = index
                tasks.remove(at: index)

                undoManager?.registerUndo(withTarget: self) { target in
                    if let task = target.deletedTask,
                       let idx = target.deletedIndex {
                        target.tasks.insert(task, at: idx)
                    }
                }
                undoManager?.setActionName("タスクを削除")
                showUndoToast = true

                DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                    showUndoToast = false
                }
            }
        }
        .overlay(alignment: .bottom) {
            if showUndoToast {
                HStack(spacing: 12) {
                    Text("削除しました")
                    Button("取り消す") {
                        undoManager?.undo()
                        showUndoToast = false
                    }
                    .fontWeight(.semibold)
                }
                .padding(.horizontal, 16)
                .padding(.vertical, 10)
                .background(.ultraThinMaterial, in: Capsule())
                .padding(.bottom, 40)
            }
        }
    }
}

要点:

  • @Environment(\.undoManager) でシステム提供の Undo 管理器を取得し、Shake to Undo などのシステムジェスチャもサポートできます
  • registerUndo(withTarget:) で逆操作を登録し、削除されたデータを元の位置に差し戻します
  • setActionName(_:) で Undo 操作の表示名を設定し、システムの Undo メニューで見えるようにします
  • 下部の非阻止型トーストが Alert を置き換えます。ユーザーは操作を続けられ、3 秒後に自動で消えます
  • 本当に不可逆な操作、たとえばデータベース削除やアカウント閉鎖のときだけ、阻止型確認を使います

責任ある権限リクエスト:Just-in-time 戦略

Responsibility 原則では、権限リクエストが正しいタイミングで起きる必要があります。SwiftUI の PhotosPicker はこの原則に自然に従います。

struct ProfileSetupView: View {
    @State private var selectedItems: [PhotosPickerItem] = []
    @State private var showPicker = false

    var body: some View {
        VStack(spacing: 20) {
            Text("プロフィール写真を設定")
                .font(.title2)

            Button("写真から選択") {
                showPicker = true
            }
            .buttonStyle(.borderedProminent)
        }
        .photosPicker(
            isPresented: $showPicker,
            selection: $selectedItems,
            matching: .images
        )
        .onChange(of: selectedItems) { _, newItems in
            guard let item = newItems.first else { return }
            loadImage(from: item)
        }
    }

    private func loadImage(from item: PhotosPickerItem) {
        item.loadTransferable(type: Data.self) { result in
            // 選択された画像データを処理する
        }
    }
}

要点:

  • 権限ダイアログはユーザーが「写真から選択」をタップしたときだけ表示され、App 起動時には出ません
  • PhotosPicker はデフォルトで Limited Access、つまり選択された写真だけを要求し、最小権限の原則に合います
  • 古い UIImagePickerController で Full Access を要求するのは避けます。レビュー上のリスクが高くなります
  • 選択後は loadTransferable で非同期にデータを読み込み、メインスレッドをブロックしません

触覚フィードバックで Craft を強化する

Craft 原則は、精密なインターフェイスフィードバックを求めます。SwiftUI の sensoryFeedback は触覚フィードバックを宣言的に扱えるようにします。

struct CheckInButton: View {
    @State private var isCheckedIn = false

    var body: some View {
        Button {
            isCheckedIn.toggle()
        } label: {
            Image(systemName: isCheckedIn ? "checkmark.circle.fill" : "circle")
                .font(.system(size: 56))
                .foregroundStyle(isCheckedIn ? .green : .gray)
                .contentTransition(.symbolEffect(.replace))
        }
        .sensoryFeedback(.success, trigger: isCheckedIn) { oldValue, newValue in
            newValue == true
        }
        .sensoryFeedback(.impact, trigger: isCheckedIn) { oldValue, newValue in
            newValue == false
        }
    }
}

要点:

  • .sensoryFeedback(_:trigger:condition:) は iOS 17 以降で利用でき、条件クロージャで発火タイミングを制御します
  • チェックイン成功には .success、取り消しには .impact を使い、状態変化を明確に区別します
  • body の中で UIImpactFeedbackGenerator を直接呼ぶと宣言的構造が壊れるため避けます
  • .contentTransition(.symbolEffect(.replace)) と組み合わせることで、アイコン切り替えアニメーションと触覚を同期できます

主要な学び

何をするか:AI 機能に「最悪ケース」チェックリストを追加する なぜ価値があるか:Session は AI 出力のリスクを予測するよう明確に求めています。レシピ App の AI がアレルギーを持つユーザーに誤った食材を推薦すれば、生命や身体の安全に関わります。これは体験の問題ではなく、責任の問題です。 どう始めるか:各 AI 機能について 3 つの問いを列挙します。この機能が悪用されたらどうなるか。誰が傷つくか。どう防ぐか。リスクを制御できないなら二次確認や人間のレビューを入れます。リスクが価値を上回るなら削除します。

何をするか:App 起動時の権限リクエストをすべて業務上のトリガー地点へ移す なぜ価値があるか:Responsibility 原則は「正しいタイミングで求める」ことを強調します。起動直後の権限ポップアップはユーザー離脱の大きな原因です。 どう始めるかAppDelegateApp の初期化コード、すべての requestAuthorization 呼び出しをスキャンします。権限リクエストを、ユーザーが対応する機能を能動的に起動したコードパスへ移します。写真権限の手動リクエストは PhotosPicker に置き換え、CoreLocationrequestWhenInUseAuthorization は地図関連画面へ入るときだけ呼びます。

何をするか:確認ダイアログの 80% を Undo に置き換える なぜ価値があるか:Agency 原則の中心は、ユーザーが安心して探索できることです。「本当に削除しますか?」という Alert は、ユーザーの忍耐と信頼を毎回消費します。 どう始めるか:プロジェクト内のすべての UIAlertControllerconfirmationDialog の利用場面を確認します。リスト項目の削除、タグ削除、お気に入り解除など回復可能な操作には、UndoManager と下部トーストを使います。データベース削除、アカウント閉鎖、送金など、本当に不可逆な操作だけ確認ダイアログを残します。

何をするか:重要操作に触覚フィードバックを追加する なぜ価値があるか:Craft 原則は「滑らかなアニメーションが即時で自然なフィードバックを提供する」ことを求めます。触覚は「実体感」を作る最も直接的な方法です。 どう始めるか:フォーム送信、チェックイン完了、メッセージ送信、スイッチ切り替えなどの重要なインタラクションに .sensoryFeedback(.success, trigger: state) を追加します。成功と失敗のフィードバック種別、たとえば .success.error を分け、ユーザーが触覚だけで結果を感じ取れるようにします。

何をするか:「一貫性監査」を行う なぜ価値があるか:Familiarity 原則は「同じ見た目のものは同じ動作をするべき」と示しています。不一致なインターフェイスはユーザーを混乱させ、学習コストを増やします。 どう始めるか:App 内のすべてのボタン、カード、リスト項目のインタラクションを一覧化します。同じ視覚スタイルのコントロールが同じ動作パターンを持っているか確認します。たとえば、取り消し線付きボタンはすべて削除操作、右上の三点メニューはすべてその他オプション、というように統一します。

関連 Session

  • Principles of great design — ブランド識別とビジュアルシステムのデザイン原則。この Session のデザイン哲学とつながる
  • SwiftUI essentials — Session 250 の Agency と Simplicity の原則を実践するための SwiftUI 新機能
  • UIKit modernization — 既存コードベースで Craft と Familiarity の原則を実装する助けになる UIKit モダナイズ
  • Accessibility Reading — アクセシブルな読書体験。Flexibility 原則におけるインクルーシブデザインに対応する
  • Agentic apps — AI Agent App のデザイン。Responsibility 原則が AI 機能の安全性で特に重要になる

コメント

GitHub Issues · utterances