iOS 26 Liquid Glass完全ガイド:SwiftUIで実装する方法とベストプラクティス

iOS 26で登場したLiquid Glassの実装方法を徹底解説。glassEffect()モディファイアからGlassEffectContainer、モーフィングトランジション、ボタンスタイル、ティンティングまで、SwiftUIでの実践的なコード例とともに紹介します。

はじめに:Liquid Glassとは何か

2025年のWWDCで、AppleがiOS 26とともにとんでもないデザインリニューアルを発表しました。その中心にあるのがLiquid Glass(リキッドグラス)。正直に言うと、iOS 7でフラットデザインに移行して以来、ここまで大きなビジュアルの変革はなかったと思います。

Liquid Glassは、単なる半透明のぼかし効果じゃありません。

これはリアルタイムで光を屈折・反射させる動的なマテリアルで、背景のコンテンツ、デバイスの動き、ユーザーのインタラクションに応じて常にその外観が変化します。まるで本物のガラスがUIの上に浮かんでいるような、物理的なリアリティを持つデザインシステムなんです。初めてシミュレータで動かしたときは、思わず「おおっ」と声が出ました。

重要なポイントとして、Liquid GlassはiOSだけに留まりません。macOS、iPadOS、watchOS、tvOS、visionOSのすべてのAppleプラットフォームで採用されていて、統一されたデザイン言語として機能します。SwiftUIで構築されたアプリは、Xcode 26で再コンパイルするだけで多くのLiquid Glass効果が自動的に適用されます。

本記事では、Liquid Glassの基本原理からSwiftUIでの具体的な実装方法、ベストプラクティス、アクセシビリティ対応まで、開発者が知るべきことを網羅的に解説します。コード例を豊富に交えながら、一緒に学んでいきましょう。

Liquid Glassの基本原理

Liquid Glassのデザインは、現実世界のガラスの光学特性をかなり忠実に再現しています。この新しいマテリアルを理解するには、4つの核心的な原理を押さえておくことが大切です。

リアルタイム光屈折(レンジング)

Liquid Glassの最も目を引く特徴は、背景コンテンツをリアルタイムで屈折させる能力です。物理的なガラスレンズと同じように、Liquid Glass要素の背後にあるコンテンツがわずかに歪んで見えます。この光学的な歪み(レンジング効果)は、要素の形状やサイズ、位置に応じて動的に計算されるんですね。ユーザーがスクロールしたりコンテンツが移動すると、屈折パターンもリアルタイムで変化します。

スペキュラーハイライト

デバイスの傾きや向きに応じて、Liquid Glass表面にスペキュラーハイライト(鏡面反射光)が表示されます。iPhoneを傾けると、ガラス要素の表面でハイライトがすーっと滑らかに移動して、物理的な深度感と立体感が生まれます。デバイスのモーションセンサーと連動しているので、まるで実際のガラスに光が当たっているかのような感覚になります。

アダプティブシャドウ

Liquid Glass要素は、背景やコンテキストに応じて適応的なシャドウを生成します。明るい背景では柔らかく微妙なシャドウ、暗い背景ではよりコントラストの強いシャドウが自動的に適用されます。これによって、要素がコンテンツ層から浮いているような深度感が自然に演出されるわけです。

インタラクティブな振る舞い

ユーザーがLiquid Glass要素に触れると、マテリアルが触覚的なフィードバックを返します。タッチ時に要素がわずかに圧縮され、光の屈折パターンが変化し、リリース時にはエラスティックなアニメーションで元の状態に戻る。このインタラクティブな振る舞いが、ユーザーに物理的な「触れている」感覚を与えてくれます。

これら4つの原理が組み合わさることで、Liquid Glassは単なるビジュアルエフェクトを超えて、環境に応じて常に変化し続ける生きたマテリアルとして機能します。

自動適用される要素

Liquid Glassの最大の利点のひとつ。それは、既存のSwiftUIアプリに対して、多くの変更がコードなしで自動的に適用されるという点です。Xcode 26でプロジェクトを再コンパイルするだけで、以下のシステムコンポーネントにLiquid Glass効果が適用されます。

  • NavigationBar — ナビゲーションバー全体がLiquid Glassマテリアルで描画されます
  • TabBar — タブバーがフローティングピル形状のLiquid Glassに変更されます
  • Toolbar — ツールバーアイテムがガラス効果付きで表示されます
  • Sheets — モーダルシートの背景がLiquid Glassマテリアルになります
  • Popovers — ポップオーバーの背景に屈折効果が適用されます
  • Menus — コンテキストメニューがLiquid Glassスタイルで表示されます
  • Alerts — アラートダイアログがガラス効果付きに変更されます
  • Search bars — 検索バーがLiquid Glassマテリアルで描画されます
  • Toggles — トグルスイッチの外観がアップデートされます
  • Sliders — スライダーのトラックとサムがガラス効果付きになります
  • Pickers — ピッカーの選択インジケーターがLiquid Glassで表示されます

つまり、標準のSwiftUI APIを使ってアプリを構築していれば、コードの変更なしでこれらの要素がLiquid Glassデザインにアップデートされます。「正しいことをすれば、自動的に報われる」というAppleのAPI設計哲学が見事に反映されていますね。

ただし、カスタムUIコンポーネントや独自のオーバーレイ、フローティング要素を使っている場合は、手動でLiquid Glass効果を適用する必要があります。では、その方法を見ていきましょう。

glassEffect()モディファイアの使い方

カスタムビューにLiquid Glass効果を適用するための主要なAPIが、.glassEffect()モディファイアです。いくつかのバリエーションがあるので、用途に応じて使い分けていきます。

基本的な使い方

最もシンプルな形式は、パラメータなしの呼び出しです。これだけで基本的なガラス効果が適用されます。

// 基本的なglassEffect(デフォルトはregularバリアント)
Text("Hello, Liquid Glass")
    .padding()
    .glassEffect()

バリアントの指定

.glassEffect()には、RegularClearの2つの主要なバリアントがあります。

// Regularバリアント(デフォルト)
// 標準的な半透明のガラス効果。ほとんどの場面で使用
Text("Regular Glass")
    .padding()
    .glassEffect(.regular)

// Clearバリアント
// より透明度の高いガラス効果。メディアリッチな背景上で使用
Text("Clear Glass")
    .padding()
    .glassEffect(.clear)

形状の指定

ガラス効果の形状はin:パラメータで指定できます。SwiftUIの標準的なShape型をそのまま使えるのがありがたいですね。

// カプセル形状のガラス効果
Text("Capsule Glass")
    .padding(.horizontal, 20)
    .padding(.vertical, 10)
    .glassEffect(.regular, in: .capsule)

// 角丸四角形のガラス効果
Text("Rounded Rectangle")
    .padding()
    .glassEffect(.regular, in: .rect(cornerRadius: 16))

// 円形のガラス効果
Image(systemName: "plus")
    .frame(width: 56, height: 56)
    .glassEffect(.regular, in: .circle)

インタラクティブ効果

iOS限定の機能として、.interactive()修飾子を追加することでタッチ時のエラスティックな反応を有効にできます。ユーザーがタッチするとガラス要素がわずかに圧縮されて、リリース時に弾むようなアニメーションで戻ります。個人的に、この「押し込む」感触がすごく気持ちいいです。

// インタラクティブなガラス効果(iOS限定)
Button("Tap Me") {
    // アクション
}
.padding()
.glassEffect(.regular.interactive())

ティントの適用

ガラス効果にカラーティントを追加して、アクセントカラーやブランドカラーを反映させることもできます。

// パープルティントのガラス効果
Button("Premium Action") {
    // アクション
}
.padding()
.glassEffect(.regular.tint(.purple.opacity(0.8)))

// ブルーティントのガラス効果
Button("Primary Action") {
    // アクション
}
.padding()
.glassEffect(.regular.tint(.blue))

実践例:フローティングアクションボタン

さて、ここまでの要素を組み合わせた実践的な例として、フローティングアクションボタンを作ってみましょう。

struct FloatingActionButton: View {
    var body: some View {
        ZStack {
            // 背景コンテンツ
            ScrollView {
                LazyVStack(spacing: 12) {
                    ForEach(0..<50) { index in
                        Text("Item \(index)")
                            .frame(maxWidth: .infinity)
                            .padding()
                            .background(.ultraThinMaterial)
                            .clipShape(.rect(cornerRadius: 12))
                    }
                }
                .padding()
            }

            // フローティングアクションボタン
            VStack {
                Spacer()
                HStack {
                    Spacer()
                    Button {
                        // 新規作成アクション
                    } label: {
                        Image(systemName: "plus")
                            .font(.title2)
                            .fontWeight(.semibold)
                            .foregroundStyle(.white)
                            .frame(width: 56, height: 56)
                    }
                    .glassEffect(
                        .regular
                            .interactive()
                            .tint(.blue),
                        in: .circle
                    )
                    .padding(.trailing, 24)
                    .padding(.bottom, 24)
                }
            }
        }
    }
}

この例では、スクロール可能なリストの上にフローティングアクションボタンを配置しています。.regularバリアント、.interactive()でタッチフィードバック、.tint(.blue)でブルーのアクセントカラー、.circleで円形の形状を指定。ユーザーがスクロールするとボタンの背景に表示されるコンテンツがリアルタイムで屈折し、デバイスを傾けるとスペキュラーハイライトが移動します。

GlassEffectContainerの活用

複数のLiquid Glass要素をグループ化する際に使うのがGlassEffectContainerです。このコンテナが、内部のガラス要素間のスペーシング、連結、モーフィング動作を管理してくれます。

なぜGlassEffectContainerが必要なのか

複数のガラス要素を単純に並べると、それぞれが独立したガラス面として描画されてしまいます。これだと視覚的に煩雑になるし、光の屈折も重複してしまう。GlassEffectContainerを使うことで、複数の要素がひとつの統合されたガラスサーフェスの一部として振る舞うようになります。

基本的な使い方

spacingパラメータで要素間の距離を指定します。この距離が近い場合、要素間のガラスが連結されて見えるモーフィング効果が発生します。

struct GlassToolbar: View {
    var body: some View {
        GlassEffectContainer(spacing: 20) {
            HStack(spacing: 16) {
                Button("Edit") {
                    // 編集アクション
                }
                .glassEffect()

                Button("Share") {
                    // 共有アクション
                }
                .glassEffect()

                Button("Delete") {
                    // 削除アクション
                }
                .glassEffect()
            }
        }
    }
}

3つのボタンがGlassEffectContainer内に配置されていて、spacing: 20でガラス要素間の相互作用を計算する基準距離を定義しています。要素が近接していると、ガラス面が視覚的に融合するような効果が得られます。

spacingの役割

GlassEffectContainerspacingパラメータは、単なるレイアウト上のスペーシングではありません。これはガラス要素間のモーフィング動作の閾値として機能するんです。要素間の実際の距離がこの値より小さい場合はガラス面が融合して見えます。値が大きければ、各要素は独立したガラス面として描画されますが、コンテナ内で統一的な光の屈折パターンを共有します。

また、コンテナ内の要素がアニメーションで移動したり表示・非表示が切り替わったりする際には、ガラス面が滑らかにモーフィングするトランジションが自動的に適用されます。要素の追加や削除がスムーズなアニメーションで視覚化されるのは、使ってみると本当に感動的です。

glassEffectIDによるモーフィングトランジション

Liquid Glassの最も印象的な機能のひとつがモーフィングトランジションです。要素が追加されたり削除されたりする際に、ガラス面が滑らかに変形してアニメーションする効果ですね。この機能を実現するのが.glassEffectID()モディファイアです。

Namespaceとの連携

.glassEffectID()は、SwiftUIの@Namespaceと連携して動作します。同じnamespace内で同一のIDを持つ要素は、表示状態が切り替わる際にモーフィングトランジションで接続されます。概念的には.matchedGeometryEffect()に似ていますが、ガラスマテリアルの光学的特性も含めたトランジションを実現してくれるところが違います。

実践例:展開可能なメニュー

タップで展開・縮小するフローティングメニューの完全な実装例を見てみましょう。

struct ExpandableMenu: View {
    @State private var isExpanded = false
    @Namespace private var namespace

    var body: some View {
        GlassEffectContainer(spacing: 20) {
            HStack(spacing: 16) {
                if isExpanded {
                    Button("Camera", systemImage: "camera") {
                        // カメラアクション
                    }
                    .glassEffect(.regular.interactive())
                    .glassEffectID("camera", in: namespace)

                    Button("Photos", systemImage: "photo") {
                        // フォトライブラリアクション
                    }
                    .glassEffect(.regular.interactive())
                    .glassEffectID("photos", in: namespace)
                }

                Button {
                    withAnimation(.bouncy) {
                        isExpanded.toggle()
                    }
                } label: {
                    Image(systemName: isExpanded ? "xmark" : "plus")
                        .frame(width: 44, height: 44)
                }
                .buttonStyle(.glassProminent)
                .buttonBorderShape(.circle)
                .glassEffectID("toggle", in: namespace)
            }
        }
    }
}

このコードの動作を詳しく見ていきましょう。

  • 初期状態では、isExpandedfalseなので、トグルボタンのみが表示されます。プラスアイコン付きの円形ガラスです。
  • タップ時withAnimation(.bouncy)でバウンスアニメーション付きの状態変更が発生。
  • 展開時には「Camera」と「Photos」のボタンが追加されます。各ボタンに.glassEffectID()が設定されているため、ガラス面がトグルボタンから滑らかにモーフィングして新しいボタンが出現します。
  • 縮小時には逆のモーフィングが発生し、展開されたボタンがトグルボタンに向かって収縮します。

GlassEffectContainerがこれらの要素をグループ化しているため、モーフィングトランジションは統一されたガラスサーフェス上で発生します。個々の要素がバラバラにアニメーションすることはありません。

ボタンスタイルの活用

iOS 26では、Liquid Glassに対応した2つの新しいボタンスタイルが導入されました。アクションの優先度に応じて使い分けましょう。

.buttonStyle(.glass)

.glassスタイルは、セカンダリ(副次的)なアクション向けです。半透明のガラス効果が適用されて、背景コンテンツが透けて見えます。視覚的に控えめな印象なので、メインのアクションではなく補助的な操作に適しています。

.buttonStyle(.glassProminent)

.glassProminentスタイルは、プライマリ(主要な)アクション用です。不透明度が高く、視覚的に強調されたガラス効果が適用されます。ユーザーの注意を引くべき重要なアクションに使いましょう。

実践例:ダイアログのボタン

キャンセルと保存のアクションを持つダイアログの実装例です。

struct SaveDialog: View {
    @Environment(\.dismiss) private var dismiss

    var body: some View {
        VStack(spacing: 24) {
            // ダイアログのコンテンツ
            VStack(spacing: 8) {
                Text("変更を保存しますか?")
                    .font(.headline)
                Text("未保存の変更があります。保存せずに閉じると、変更は失われます。")
                    .font(.subheadline)
                    .foregroundStyle(.secondary)
                    .multilineTextAlignment(.center)
            }
            .padding(.horizontal)

            // アクションボタン
            HStack(spacing: 16) {
                // セカンダリアクション:glassスタイル(半透明)
                Button("キャンセル") {
                    dismiss()
                }
                .buttonStyle(.glass)

                // プライマリアクション:glassProminentスタイル + ティント
                Button("保存") {
                    // 保存処理
                    dismiss()
                }
                .buttonStyle(.glassProminent)
                .tint(.blue)
            }
        }
        .padding(24)
    }
}

ポイントは、アクションの重要度でスタイルを使い分けているところ。「キャンセル」は副次的なアクションなので.glassで控えめに、「保存」は主要なアクションなので.glassProminent.tint(.blue)で視覚的に強調しています。この使い分けによって、ユーザーは直感的にどちらが主要なアクションか判断できるわけです。

ティンティングのベストプラクティス

Liquid Glassにおけるティント(色付け)は、UIの階層と重要度を伝える強力なツールです。ただし、使い方を誤ると視覚的な混乱を招きます。ここはしっかり押さえておきたいところです。

基本ルール

ティントはプライマリアクションにのみ適用します。セカンダリやターシャリのアクションには決してティントを適用しないでください。すべてのボタンにティントを付けてしまうと、どれが重要なのかまったく判別できなくなります。

正しい使い方と誤った使い方

// ✅ 正しい:プライマリアクションのみにティント
HStack(spacing: 16) {
    Button("キャンセル") { }
        .buttonStyle(.glass)
        // ティントなし — セカンダリアクション

    Button("送信") { }
        .buttonStyle(.glassProminent)
        .tint(.blue)
        // ティントあり — プライマリアクション
}

// ❌ 誤り:すべてのボタンにティント
HStack(spacing: 16) {
    Button("キャンセル") { }
        .buttonStyle(.glass)
        .tint(.red)  // セカンダリにティントを適用してはいけない

    Button("送信") { }
        .buttonStyle(.glassProminent)
        .tint(.blue)
}

ティントカラーの選択にも注意が必要です。アプリのアクセントカラーを基調として、画面内で使用するティントカラーは最大でもひとつに留めましょう。複数の異なるティントカラーが混在すると、情報の階層が不明確になってしまいます。

RegularバリアントとClearバリアントの使い分け

Liquid GlassにはRegularClearの2つのバリアントがありますが、使い分けは明確に定義されています。

Regularバリアント

.regularデフォルトのバリアントで、ほとんどの場面で使うべきものです。ナビゲーションバー、ツールバー、フローティングボタン、メニューなど、標準的なUI要素にはすべてこちらを使います。適度な半透明性を持ち、背景コンテンツとの十分なコントラストを確保しながらLiquid Glassの光学効果を表現してくれます。

Clearバリアント

.clearバリアントは、かなり限定された条件下でのみ使います。具体的には、以下の3つの条件がすべて満たされた場合にのみ適切です。

  1. メディアリッチな背景が存在する — 写真、動画、グラデーションなど、視覚的に豊かなコンテンツが背景にある
  2. 背景コンテンツがディミング(減光)の影響を受けにくい — Regularバリアントの減光が背景コンテンツの体験を損なう場合
  3. ガラス上のコンテンツが太く明るい — アイコンやテキストが十分なコントラストを持ち、より透明なガラス上でも視認性を確保できる

典型的な使用例は、写真閲覧アプリやメディアプレイヤーのオーバーレイコントロールです。写真の上にフローティングするコントロールバーに対してRegularバリアントを適用すると写真が過度に暗くなってしまう。そんなときにClearバリアントの出番です。

決して混在させないこと

同じ画面内でRegularバリアントとClearバリアントを混在させてはいけません。これは非常に重要なルールです。2つのバリアントは異なる透明度と光学特性を持つため、混在すると視覚的な不整合が目立ちます。画面内のすべてのLiquid Glass要素は、統一されたバリアントで描画してください。

実践的なプロジェクト:カスタムツールバーの構築

ここまでの知識を総動員して、実践的なカスタムフローティングツールバーを作ってみましょう。複数のアクションボタンとモーフィングトランジションを組み合わせた、かなり現実的なユースケースです。

struct CustomFloatingToolbar: View {
    @State private var selectedTool: String? = nil
    @State private var showColorPicker = false
    @Namespace private var toolbarNamespace

    var body: some View {
        ZStack(alignment: .bottom) {
            // メインコンテンツエリア
            Canvas { context, size in
                // 描画キャンバス
            }
            .ignoresSafeArea()

            // フローティングツールバー
            GlassEffectContainer(spacing: 16) {
                VStack(spacing: 12) {
                    // カラーピッカー(展開時のみ表示)
                    if showColorPicker {
                        HStack(spacing: 12) {
                            ForEach(
                                [Color.red, .orange, .yellow,
                                 .green, .blue, .purple],
                                id: \.self
                            ) { color in
                                Circle()
                                    .fill(color)
                                    .frame(width: 32, height: 32)
                                    .onTapGesture {
                                        withAnimation(.bouncy) {
                                            showColorPicker = false
                                        }
                                    }
                                    .glassEffect(.regular.interactive())
                            }
                        }
                        .glassEffectID("colorPicker", in: toolbarNamespace)
                        .transition(.scale.combined(with: .opacity))
                    }

                    // メインツールバー
                    HStack(spacing: 16) {
                        // ペンツール
                        ToolButton(
                            icon: "pencil.tip",
                            isSelected: selectedTool == "pen",
                            namespace: toolbarNamespace,
                            id: "pen"
                        ) {
                            withAnimation(.bouncy) {
                                selectedTool = "pen"
                            }
                        }

                        // 消しゴムツール
                        ToolButton(
                            icon: "eraser",
                            isSelected: selectedTool == "eraser",
                            namespace: toolbarNamespace,
                            id: "eraser"
                        ) {
                            withAnimation(.bouncy) {
                                selectedTool = "eraser"
                            }
                        }

                        // カラーピッカートグル
                        Button {
                            withAnimation(.bouncy) {
                                showColorPicker.toggle()
                            }
                        } label: {
                            Image(systemName: "paintpalette")
                                .frame(width: 44, height: 44)
                        }
                        .buttonStyle(.glass)
                        .glassEffectID("palette", in: toolbarNamespace)

                        Divider()
                            .frame(height: 24)

                        // 取り消しボタン
                        Button {
                            // 取り消し処理
                        } label: {
                            Image(systemName: "arrow.uturn.backward")
                                .frame(width: 44, height: 44)
                        }
                        .buttonStyle(.glass)
                        .glassEffectID("undo", in: toolbarNamespace)

                        // やり直しボタン
                        Button {
                            // やり直し処理
                        } label: {
                            Image(systemName: "arrow.uturn.forward")
                                .frame(width: 44, height: 44)
                        }
                        .buttonStyle(.glass)
                        .glassEffectID("redo", in: toolbarNamespace)
                    }
                }
            }
            .padding(.bottom, 40)
        }
    }
}

struct ToolButton: View {
    let icon: String
    let isSelected: Bool
    let namespace: Namespace.ID
    let id: String
    let action: () -> Void

    var body: some View {
        Button(action: action) {
            Image(systemName: icon)
                .frame(width: 44, height: 44)
        }
        .buttonStyle(isSelected ? .glassProminent : .glass)
        .glassEffectID(id, in: namespace)
    }
}

この実装では、以下のLiquid Glass技法を組み合わせています。

  • GlassEffectContainerでツールバー全体をグループ化し、統一されたガラスサーフェスを形成
  • glassEffectIDで各ボタンにIDを付与し、状態変化時のモーフィングトランジションを実現
  • buttonStyle(.glass).glassProminentの使い分けで選択状態を視覚的に表現
  • withAnimation(.bouncy)でバウンスアニメーション付きの状態遷移
  • カラーピッカーの展開・縮小がモーフィングトランジションでアニメーション

こうしたカスタムツールバーは、お絵描きアプリや写真編集アプリ、ノートアプリなどのクリエイティブツール系アプリで特に効果を発揮します。Liquid Glassのトランスルーセントな特性によって、ツールバーが常に背景のキャンバスと調和して没入感のある編集体験を提供してくれます。

アクセシビリティ対応

Liquid Glassの大きな利点として見逃せないのが、アクセシビリティへの配慮がシステムレベルで組み込まれている点です。開発者が追加コードを書かなくても、ユーザーのアクセシビリティ設定に応じてLiquid Glassの表現が自動的に調整されます。

透明度を下げる(Reduce Transparency)

ユーザーが「設定」から「透明度を下げる」を有効にすると、Liquid Glassはより不透明な「フロスティ」(曇りガラス)外観に変化します。背景コンテンツの屈折効果は抑制されて、ガラス面はより均一な半透明面として描画されます。透明効果が視覚的に難しいユーザーでも、UI要素の境界を明確に認識できるようになるわけです。

コントラストを上げる(Increase Contrast)

「コントラストを上げる」設定が有効な場合、Liquid Glass要素は透明なガラス効果の代わりに黒または白の不透明な背景にボーダー付きで表示されます。UI要素と背景のコントラスト比が大幅に向上するので、ロービジョンのユーザーでもインターフェースを明確に認識できます。

視覚効果を減らす(Reduce Motion)

「視覚効果を減らす」設定が有効な場合、Liquid Glassのエラスティックなインタラクション効果が無効化されます。タッチ時の圧縮アニメーションやバウンスエフェクトは表示されず、モーフィングトランジションもクロスフェードなどのシンプルなアニメーションに置き換えられます。

開発者に求められること

ここで強調しておきたいのは、これらのアクセシビリティ対応はすべて自動だということ。.glassEffect()モディファイアを使っている限り、追加のアクセシビリティコードは一切不要です。システムがユーザーの設定を検知して、適切な代替表現を自動的に提供してくれます。

ただし、カスタムビューでLiquid Glassの外観を模倣するような独自実装を行っている場合は、これらの設定を自分で検知して対応する必要があります。だからこそ、可能な限り公式のAPIを使うことが推奨されます。

デザインのDo'sとDon'ts

Liquid Glassを効果的に使うためには、Appleのデザインガイドラインに従うことが重要です。ここでは推奨される使い方と避けるべき使い方をまとめます。

Do's(推奨事項)

  • GlassEffectContainerで複数の要素をグループ化する — 複数のガラス要素を配置する場合、必ずGlassEffectContainerで囲みましょう。統一されたガラスサーフェスとモーフィング動作が実現されます。
  • .glassEffectID()でモーフィングトランジションを適用する — 要素の表示・非表示が切り替わる場面では、これを使って滑らかなモーフィングアニメーションを実現しましょう。正直、これがLiquid Glassの最も魅力的な機能だと思います。
  • iOSでは.interactive()を使用する — タッチ可能な要素には.interactive()を追加してタッチフィードバックを有効に。ユーザーに物理的な操作感を提供できます。
  • プライマリアクションにのみティントを適用する — ティントは情報の階層を伝えるもの。最も重要なアクションのみに適用しましょう。
  • コンテンツの上にフローティングするナビゲーション層にガラスを使用する — ツールバー、フローティングボタン、オーバーレイコントロールなどが最適な用途です。

Don'ts(禁止事項)

  • コンテンツ層にガラス効果を適用しない — リスト、カード、テーブルセルなどのコンテンツ要素にLiquid Glassを適用してはいけません。ガラス効果はナビゲーション層のための素材です。コンテンツに適用すると情報の読み取りが困難になります。
  • コンテナなしでガラスの上にガラスを重ねないGlassEffectContainerなしでガラス要素を重ねると、光の屈折が二重に適用されて視覚的なアーティファクトが発生します。
  • RegularとClearバリアントを混在させない — 同じ画面内で2つのバリアントを混ぜると、透明度の不整合が目立ちます。画面単位でひとつのバリアントに統一しましょう。
  • セカンダリ・ターシャリアクションにティントを適用しない — 重要でないアクションにティントを適用すると、視覚的な階層が壊れます。
  • フルスクリーンの背景にガラスを使用しない — Liquid Glassは「浮遊する要素」として設計されています。画面全体の背景に適用するとパフォーマンス問題が発生するだけでなく、デザインの意図にも反します。

実例で見るDo'sとDon'ts

// ✅ 正しい:ナビゲーション層にガラスを使用
struct CorrectUsage: View {
    var body: some View {
        ZStack(alignment: .bottom) {
            // コンテンツ層(ガラス効果なし)
            ScrollView {
                LazyVStack(spacing: 8) {
                    ForEach(items) { item in
                        ItemRow(item: item)
                            // ⚠️ ここにglassEffectを適用してはいけない
                    }
                }
            }

            // ナビゲーション層(ガラス効果あり)
            GlassEffectContainer(spacing: 16) {
                HStack(spacing: 12) {
                    Button("Filter") { }
                        .glassEffect(.regular.interactive())
                    Button("Sort") { }
                        .glassEffect(.regular.interactive())
                }
            }
        }
    }
}

// ❌ 誤り:コンテンツ層にガラスを使用
struct IncorrectUsage: View {
    var body: some View {
        ScrollView {
            LazyVStack(spacing: 8) {
                ForEach(items) { item in
                    ItemRow(item: item)
                        .glassEffect()  // コンテンツにガラスを適用しない!
                }
            }
        }
    }
}

Liquid Glassの適切な使用場所はナビゲーション層のフローティング要素です。コンテンツそのものにガラス効果を適用すると、テキストの読みやすさが低下してパフォーマンスも悪化します。常に「このUI要素はコンテンツ層なのか、ナビゲーション層なのか」を意識して判断してください。

パフォーマンスに関する考慮事項

Liquid Glassはリアルタイムの光学シミュレーションを行うため、パフォーマンスへの影響も意識しておく必要があります。

まず、ガラス要素の数を最小限に抑えること。画面上に表示されるLiquid Glass要素が増えるほどGPUの負荷が上がります。必要な要素にのみガラス効果を適用して、装飾的な目的での乱用は避けましょう。

次に、GlassEffectContainerを積極的に使うこと。コンテナを使うとシステムが複数のガラス要素を効率的にバッチ処理できるので、個別にガラス効果を適用するよりパフォーマンスが向上します。

そして、スクロールビュー内の大量の要素にガラス効果を適用しないこと。スクロール中はフレームごとにガラスの屈折計算が行われるため、大量のガラス要素がスクロールするとフレームレートが落ちる可能性があります。ここは特に気をつけたいポイントですね。

まとめ

Liquid Glassは、iOS 26をはじめとするAppleの全プラットフォームに導入された革新的なデザインシステムです。この記事で解説した内容を振り返ってみましょう。

  • 基本原理 — リアルタイム光屈折、スペキュラーハイライト、アダプティブシャドウ、インタラクティブな振る舞いの4本柱
  • 自動適用 — NavigationBar、TabBar、Toolbarなど標準UIコンポーネントはコード変更なしでLiquid Glassが適用される
  • .glassEffect() — カスタムビューにガラス効果を適用する主要API。Regular/Clearバリアント、形状、インタラクティブ、ティントを指定可能
  • GlassEffectContainer — 複数のガラス要素をグループ化して統一されたサーフェスとモーフィング動作を実現
  • glassEffectID — Namespaceと連携して要素間のモーフィングトランジションを実現
  • ボタンスタイル.glass(セカンダリ)と.glassProminent(プライマリ)の使い分け
  • ティンティング — プライマリアクションにのみ適用
  • バリアント選択 — Regularがデフォルト、Clearはメディアリッチな背景上でのみ
  • アクセシビリティ — 透明度の低減、コントラスト向上、モーション軽減が自動対応
  • デザインルール — ナビゲーション層にのみ適用し、コンテンツ層には使わない

Liquid Glassは単なるビジュアルアップデートではありません。ユーザーインターフェースが環境とインタラクションに応じて生きているかのように反応する、新しいパラダイムの始まりだと感じています。リアルタイムの光学シミュレーション、物理ベースのインタラクション、シームレスなモーフィングトランジションが融合して、これまでにないレベルの没入感とエレガンスをアプリにもたらしてくれます。

SwiftUIとの深い統合のおかげで、比較的少ないコードでこれらの高度なビジュアルエフェクトを実現できます。まずはXcode 26で既存プロジェクトを再コンパイルするところから始めて、段階的にカスタムのLiquid Glass実装を追加していくのがおすすめです。

Appleは今後もLiquid GlassのAPIを拡張していくでしょう。visionOSにおける空間コンピューティングとの統合や、より高度な光学シミュレーション、カスタムマテリアルの定義機能など、さらなる進化が期待されます。今のうちにLiquid Glassの基礎を固めて、次世代のAppleプラットフォーム開発に備えていきましょう。

著者について Editorial Team

Our team of expert writers and editors.