mcpField
M
macos-settings-ui

macos-settings-ui

github.com/fayazaraデザイン・UI685660 回インストール22 週間前に更新
リポジトリ

macos-settings-ui について

macOS 26のデザイン指針に沿ったネイティブ設定ウィンドウの実装ガイドです。macOSアプリ開発者向けに、NSWindowControllerとNavigationSplitViewでLiquid Glassの外観とサイドバー形式の設定画面を構成します。

主な機能

  1. 01NSWindowControllerによるウィンドウ構成
  2. 02サイドバーと詳細画面のナビゲーション
  3. 03Liquid Glassのウィンドウ外観
  4. 04透明背景のグループ化フォーム
  5. 05メニューバーアプリのアクティベーションポリシー

活用シーン

  1. 01macOSアプリのネイティブ設定ウィンドウ作成
  2. 02カテゴリ別の設定ページと詳細ペインの整理
  3. 03メニューバーアプリから設定画面を開く
  4. 04Toggle、Picker、Sliderを含むフォームの追加

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/fayazara/macos-app-skills --skill macos-settings-ui

インストール後、Claude Code で/macos-settings-uiを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
ExampleDetailPane.swift
SettingsView.swift
SettingsWindowController.swift

詳細

Liquid Glass を使用した macOS の設定 UI

このスキルでは、Apple の macOS 26 デザインガイドラインに沿ったネイティブの macOS 設定ウィンドウを作成します。結果は、Liquid Glass のウィンドウクローム、戻る/進むツールバーナビゲーション、背景が透明なグループ化された Form セクション、適切なスクロール端のエフェクトを備えた、サイドバーと詳細ペインの NavigationSplitView です。

アーキテクチャの概要

設定 UI は、次の 3 つのレイヤーで構成されます。

  1. ウィンドウコントローラー(SettingsWindowController.swift)— .fullSizeContentView を使って NSWindow をプログラムで作成する NSWindowController です。これにより、ウィンドウの角が丸い Liquid Glass になります。この効果は SwiftUI の Window シーンでは実現できません。

  2. ルートビュー(SettingsView.swift)— サイドバーリストと詳細ペインを備えた NavigationSplitView です。ツールバーに戻る/進むナビゲーション履歴が含まれており、これによって NSToolbar も作成されます(Liquid Glass のタイトルバーバー表示に必要です)。

  3. 詳細ペイン(タブごとに 1 ファイル)— 各ペインでは Form { Section(...) { ... } }.formStyle(.grouped).scrollContentBackground(.hidden) を使用します。

SwiftUI の Window シーンではなく NSWindowController を使う理由

SwiftUI の宣言型 Window シーンでは、NSWindow のスタイルマスクを公開していません。macOS 26 で Liquid Glass のウィンドウクローム(角丸、半透明のサイドバー、ぼかしのかかったタイトルバー)を表示するには、ウィンドウの作成時に .fullSizeContentView フラグを設定する必要があります。NSViewRepresentable を使って後から設定しようとしても、SwiftUI がウィンドウの設定をリセットするため、確実ではありません。

NSWindowController を使う方法では、ツールバースタイル、フレームの自動保存、最小サイズ、デリゲートのライフサイクルも直接制御できます。

重要なモディファイア

すべての詳細ペインの Form には、次の 3 つのモディファイアを必ず設定してください。

.formStyle(.grouped)
.scrollContentBackground(.hidden)
.contentMargins(.top, 8, for: .scrollContent)
  • .formStyle(.grouped) — ネイティブのインセットされた角丸長方形のセクション表示になります
  • .scrollContentBackground(.hidden) — Form の背景を透明にして、Liquid Glass のウィンドウクロームが透けて見えるようにします。これがないと、不透明な白色または暗色の背景になり、Glass の効果が損なわれます。
  • .contentMargins(.top, 8) — ツールバーと最初のセクションの間に余白を追加します

サイドバーの List には、必ず次のモディファイアを設定してください。

.listStyle(.sidebar)
.scrollEdgeEffectStyleSoftIfAvailable()  // macOS 26 progressive blur at scroll edges
.navigationTitle("Settings")

NavigationSplitView には、必ず次のモディファイアを設定してください。

NavigationSplitView(columnVisibility: .constant(.all))  // sidebar always visible
// ...
.navigationTitle("Settings")
.navigationSplitViewStyle(.balanced)

参考実装

完全に動作するコードについては、参考ファイルを参照してください。

  • references/SettingsWindowController.swift — ウィンドウコントローラー(そのままコピーし、アクティベーションポリシーの呼び出しをアプリに合わせて調整してください)
  • references/SettingsView.swift — サイドバー、詳細ペインへのルーティング、ナビゲーション履歴を備えたルートビュー
  • references/ExampleDetailPane.swift — 一般的なコントロールパターン(Toggle、Picker、Slider、LabeledContent)を示す詳細ペインのテンプレート

手順:新しいアプリに設定画面を追加する

1. タブの Enum を作成する

設定カテゴリを定義します。各ケースにはタイトルと SF Symbol アイコンが必要です。

enum SettingsTab: String, CaseIterable, Identifiable {
    case general
    case appearance
    case about

    var id: Self { self }

    var title: String {
        switch self {
        case .general: "General"
        case .appearance: "Appearance"
        case .about: "About"
        }
    }

    var systemImage: String {
        switch self {
        case .general: "gearshape"
        case .appearance: "paintbrush"
        case .about: "info.circle"
        }
    }
}
2. ウィンドウコントローラーを作成する

references/SettingsWindowController.swift をプロジェクトにコピーします。次の項目を調整してください。

  • 初期の contentRect サイズ(デフォルトの 700x540 はほとんどのアプリに適しています)
  • minSize(デフォルトは 620x460 です)
  • アクティベーションポリシーの呼び出し(AppActivationPolicy.enter()/leave())— アプリがメニューバー専用でない場合は削除してください
3. ルート設定ビューを作成する

references/SettingsView.swift をコピーします。次の項目を調整してください。

  • SettingsTab のケースをカテゴリに合わせる
  • SettingsDetailView の switch が各ペインを返すようにする
4. 詳細ペインを作成する

タブごとにペインのファイルを作成します。次のテンプレートを使用してください。

import SwiftUI

struct GeneralSettingsPane: View {
    @AppStorage("someKey") private var someValue = false

    var body: some View {
        Form {
            Section("Section Name") {
                Toggle("Toggle label", isOn: $someValue)
            }
        }
        .formStyle(.grouped)
        .scrollContentBackground(.hidden)
        .contentMargins(.top, 8, for: .scrollContent)
    }
}
5. アプリから設定画面を開く

メニューバー、ボタン、または任意の場所から開きます。

SettingsWindowController.show(tab: .general)

SwiftUI の Window シーンは使用しないでください。openWindow(id:) も使用しないでください。すべてウィンドウコントローラーが処理します。

6. SwiftUI の Window シーンを削除する

App 構造体に Window("Settings", id: "SETTINGS") シーンがある場合は、完全に削除してください。SettingsWindowController がその代わりになります。

フォームセクション内でよく使うコントロールパターン

説明付きのトグル:

Toggle(isOn: $value) {
    VStack(alignment: .leading, spacing: 2) {
        Text("Primary label")
        Text("Description text explaining what this does.")
            .font(.caption)
            .foregroundStyle(.secondary)
    }
}
.toggleStyle(.switch)

ピッカー(ドロップダウン):

Picker("Label", selection: $value) {
    Text("Option A").tag(OptionEnum.a)
    Text("Option B").tag(OptionEnum.b)
}
.pickerStyle(.menu)

ピッカー(セグメント式):

Picker("Label", selection: $value) {
    ForEach(SomeEnum.allCases) { item in
        Text(item.title).tag(item)
    }
}
.pickerStyle(.segmented)

値ラベル付きスライダー:

LabeledContent("Size") {
    HStack(spacing: 12) {
        Slider(value: $size, in: 24...96, step: 2)
            .frame(width: 180)
        Text("\(Int(size)) pt")
            .monospacedDigit()
            .foregroundStyle(.secondary)
            .frame(width: 46, alignment: .trailing)
    }
}

ボタン行:

HStack(spacing: 8) {
    Button("Action") { doSomething() }
        .controlSize(.small)
    Button("Reset") { reset() }
        .controlSize(.small)
        .disabled(isDefault)
}

メニューバーのみのアプリ

アプリで NSApp.setActivationPolicy(.accessory) を使用している場合(Dock アイコンなし)、設定ウィンドウを開いたときにアプリが前面に表示されるよう、アクティベーションポリシーを管理する必要があります。リファレンスの SettingsWindowController は、ウィンドウの表示時に AppActivationPolicy.enter() を、閉じるときに .leave() を呼び出します。これを、次のようなシンプルな参照カウント方式のラッパーとして実装します。

@MainActor
enum AppActivationPolicy {
    private static var count = 0

    static func enter() {
        count += 1
        NSApp.setActivationPolicy(.regular)
        NSApp.activate(ignoringOtherApps: true)
    }

    static func leave() {
        count = max(0, count - 1)
        guard count == 0 else { return }
        Task { @MainActor in
            NSApp.setActivationPolicy(.accessory)
        }
    }
}

アプリが常に Dock に表示される場合は、ウィンドウコントローラーから AppActivationPolicy の呼び出しを削除し、showWindow で NSApp.activate(ignoringOtherApps: true) を使用してください。

macOS バージョンの互換性

scrollEdgeEffectStyle(.soft) API は macOS 26 以降でのみ使用できます。必ず利用可能性を確認する条件で囲んでください。

private extension View {
    @ViewBuilder
    func scrollEdgeEffectStyleSoftIfAvailable() -> some View {
        if #available(macOS 26.0, *) {
            scrollEdgeEffectStyle(.soft, for: .all)
        } else {
            self
        }
    }
}

それ以外のパターン(.fullSizeContentView を使った NSWindowController、グループ化された Form、NavigationSplitView)は macOS 14 以降で動作します。

コメント