WWDC Quick Look 💓 By SwiftGGTeam
Build a SwiftUI app with the new design

Build a SwiftUI app with the new design

元の動画を見る

ハイライト

Franck は Landmarks サンプルプロジェクトを使って、Xcode 26 SDK 環境下で SwiftUI アプリを再構築する方法をデモンストレーションしました。App 構造、Toolbars、Search、Controls、そしてカスタム Liquid Glass の 5 つの側面における変更をカバーしています。

主要内容

新しい SDK がリリースされるたび、開発者が最も恐れるのは「見た目は良くなったけど、コードを一から書き直さなければならない」という状況です。今回の iOS 26 / macOS Tahoe での Liquid Glass 導入でも、最初はそう思いがちです。sidebar がガラス素材になり、tab bar が浮き上がり、ボタンがカプセル形状になった——既存の toolbar、sheet、button をすべて書き直す必要があるのでしょうか?

Franck の答えはこうです。まず Xcode 26 で再ビルドして、何が無料で手に入るかを確認してから、コードを書くかどうかを決めましょう。彼は実際の Landmarks サンプルアプリを使って一通り説明しました——hero 画像が sidebar で切り取られてしまう、toolbar 上の 5 つのボタンの視覚的な優先順位が判別できない、検索ボックスの配置位置が iPhone と iPad で不一致になるといった問題です。これらは旧 API では手動で適応コードを書く必要がありましたが、新しい SDK では 1〜2 行の modifier で解決できます。backgroundExtensionEffect で画像を自動的に safe area の外に延伸させ、ToolbarSpacer で toolbar item を意味的にグループ化し、searchableNavigationSplitView に配置すれば、iPhone の下部と iPad の上部 trailing に自動的に適応されます。デモ全体のロジックは非常に明確です。自動的に得られる改善を先に取り入れ、残りの意味表現が必要な場合だけ新しい API を記述します。

詳細

App 構造:sidebar はデフォルトで Liquid Glass になり、コンテンツの上に浮きます。sidebar が hero 画像を切り取ってしまう場合、backgroundExtensionEffect を使って画像を safe area の外までミラー延伸させます(03:59)。

// Extend background images

struct LandmarkDetailView: View {
    let landmark: Landmark

    var body: some View {
        ScrollView {
            VStack {
                Image(landmark.backgroundImageName)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .backgroundExtensionEffect()
            }
        }
    }
}

ポイント:

  • .backgroundExtensionEffect() は画像が safe area の外でミラー表示されぼかしが入るようにし、元のコンテンツは safe area 内に表示されたままにします。
  • sidebar の幅を手動で計算したり、画像を再トリミングする必要はありません。sidebar を非表示にすると、ミラー領域が自動的に拡張されていることが確認できます。

TabView の tab bar は iPhone でフローティングスタイルになり、スクロール時に収納するよう設定でき、上部に accessory(Music の再生バーのような)を配置できます(05:39)。

// Tab bar accessory

TabView {
    // tabs
}
.tabBarMinimizeBehavior(.onScrollDown)
.tabViewBottomAccessory {
    MusicPlaybackView()
}

struct MusicPlaybackView: View {
    @Environment(\.tabViewBottomAccessoryPlacement)
    var placement

    var body: some View {
        if placement == .inline {
            // compact layout
        } else {
            // full layout
        }
    }
}

ポイント:

  • tabBarMinimizeBehavior(.onScrollDown) は、下方向スクロール時に tab bar を収納し、上方向スクロール時に復元します。
  • tabViewBottomAccessory は tab bar の上に常駐 view を配置し、bar 収納時に空いたスペースを活用します。
  • 環境値 tabViewBottomAccessoryPlacement.inline(収納時のインライン配置)と展開時の 2 つのレイアウトを区別します。

Toolbars:item は自動的にグループ化され、ToolbarSpacer で意味的な境界を明示的に制御します(08:26)。

// Visually separate toolbar items

struct LandmarkDetailView: View {
    var body: some View {
        ScrollView {
            ScrollContent()
        }
        .toolbar {
            ToolbarItem { ShareLink() }
            ToolbarSpacer(.fixed)
            ToolbarItem { FavoriteButton() }
            ToolbarItem { CollectionsButton() }
            ToolbarSpacer(.fixed)
            ToolbarItem { InspectorToggle() }
        }
    }
}

ポイント:

  • デフォルトでは隣接する ToolbarItem は同じ glass 背景グループにマージされます。
  • ToolbarSpacer(.fixed) で背景グループを強制的に分割し、favorite / collections を単独のグループに、share と inspector をそれぞれ独立させます。
  • ToolbarSpacer(.flexible, placement: .bottomBar) で leading/trailing 方向の伸長も可能です(Mail の filter と compose が bottomBar の両端に配置されるように)。
  • 単一の item を背景グループから独立させたい場合(アバターなど)は .sharedBackgroundVisibility(.hidden) を使用。item にバッジを付けたい場合は .badge(count) を使います。

SearchsearchableNavigationSplitView に配置すると、システムは iPhone では自動的に下部に、iPad/Mac では上部 trailing に配置します(11:44)。

// Search in the top-trailing position

struct TopTrailingSearch: View {
    @State private var searchText = ""

    var body: some View {
        NavigationSplitView {
            SidebarContent()
        } detail: {
            DetailContent()
        }
        .searchable(text: $searchText)
    }
}

ポイント:

  • searchableNavigationSplitView に配置することは、検索が特定の列ではなくビュー全体に作用することを示します。
  • iPhone フォーマットでは検索ボックスが自動的に画面下部に移動し、親指で到達しやすくなります。iPad/Mac では自動的に toolbar の trailing に配置されます。
  • 複数 tab のアプリで専用の検索ページを作りたい場合:Tab(role: .search) { ... } + searchableTabView に配置。ユーザーがその tab に切り替えると、検索ボックスが tab bar を置き換えます。
  • toolbar button として強制的に最小化したい場合は、.searchToolbarBehavior(.minimize) を使います。

Controls:ボタンはデフォルトでカプセル形状(小さいサイズの macOS では角丸矩形のまま)。新しい .glass / .glassProminent スタイルが追加されました(15:33)。

// Prominent glass button
Button("Get Started") { }
    .buttonStyle(.glassProminent)

// Standard glass button
Button("Learn More") { }
    .buttonStyle(.glass)

ポイント:

  • .glassProminent は行動喚起(CTA)に使用し、自動的に tint が付きます。
  • .glass は通常の glass ボタンで、背景は下層コンテンツに応じて適応します。
  • Slider に tick marks(step に基づいて自動生成)と neutralValue(0 点のオフセットを指定、EQ のような双方向値用)が追加されました。
  • .rect(corner: .containerConcentric) で角の同心円を作成し、角丸半径を手動で計算する手間を省けます。

カスタム Liquid Glass:任意の view に .glassEffect() を追加。複数の要素は GlassEffectContainer で組み合わせて morphing を実現します(19:51)。

// GlassEffectContainer

@Namespace var namespace

GlassEffectContainer {
    VStack {
        if isExpanded {
            VStack(spacing: 16) {
                ForEach(badges) { badge in
                    BadgeLabel(badge: badge)
                        .glassEffect()
                        .glassEffectID(badge.id, in: namespace)
                }
            }
        }

        BadgeToggle()
            .buttonStyle(.glass)
            .glassEffectID("badgeToggle", in: namespace)
    }
}

ポイント:

  • GlassEffectContainer は内部の複数の glass 要素がサンプリング領域を共有し、各要素が独立してサンプリングすることによる視覚的な不整合を回避します。
  • glassEffectID と同一の @Namespace を組み合わせることで、展開/収納時に要素間で流体 morph 遷移が発生します。
  • 単一の要素には .glassEffect(.regular.tint(.green)) で着色、.glassEffect(.regular.interactive()) で押下時にガラスが縮小してハイライトする効果を追加できます。

重要ポイント

  • やるべきこと:まずプロジェクトを Xcode 26 で再ビルドし、どの UI が自動的に良くなり、哪些が壊れたかを記録してください。

    • なぜやるべきか:多くの新効果(toolbar グループ化、sheet 埋め込み glass、scroll edge effect)はコードなしで得られます。一度通すだけで移作業の半分を省けます。
    • どう始めるか:新規ブランチで Xcode 26 に切り替え、各主要ページを build & run して、視覚的な変化のスクリーンショット比較を review ドキュメントにまとめ、問題リストに基づいてどの API を操作するかを決定します。
  • やるべきこと:旧バージョンで toolbar/sheet のデフォルトスタイルに対抗するために追加した hack(カスタム背景、半透明マスク、presentationBackground)をクリアしてください。

    • なぜやるべきか:新デザインの automatic scroll edge effect と Liquid Glass マテリアルはこれらの旧 hack と干渉し合い、残せば残すほど視覚的に悪化します。
    • どう始めるか:プロジェクト内で presentationBackground.background(.regularMaterial)、カスタム Color.black.opacity などを検索し、削除してデフォルト効果を確認できるかを逐一評価します。
  • やるべきこと:toolbar item を意味的に再グループ化し、ToolbarSpacer(.fixed) で明示的に表現してください。

    • なぜやるべきか:ユーザーは新デザインで glass 背景の連続性からどのボタンが同じグループかを判断します(「favorite + add to collection」は一組、share と inspector はそれぞれ独立)。意味的な不透明なツールバーはユーザーの誤操作を招きます。
    • どう始めるか:各ページの toolbar の全ボタンをリスト化し、「関連するアクション」で括弧付け;各グループ間に ToolbarSpacer(.fixed) を挿入;グループから外れる item には .sharedBackgroundVisibility(.hidden) を追加。
  • やるべきことsearchableNavigationSplitView の階層に引き上げ、iPhone/iPad 用の二重レイアウトを削除してください。

    • なぜやるべきか:システムは iPhone で自動的に検索ボックスを下部に、iPad/Mac で toolbar trailing に配置し、一つのコードで両端に対応します。
    • どう始めるか:既存の searchable を detail 列から NavigationSplitView の外側に移動;検索が中核機能でない場合は .searchToolbarBehavior(.minimize) を追加して自動収納を有効にします。
  • やるべきこと:自作コントロールの外層で glassEffect + GlassEffectContainer を試し、手書きのカードスタイルを置き換えられるか確認してください。

    • なぜやるべきか:カスタムコントロールに glass を被せるとシステムコンポーネントと視覚的に統一でき、GlassEffectContainer が提供する fluid morphing は手書きアニメーションでは実現が困難です。
    • どう始めるか:「展開/収納」インタラクションを持つ自作コンポーネント(フローティングツールバー、絵文字パネルなど)を一つ選び、各サブ要素に glassEffectID を追加、外層を GlassEffectContainer で包み、新旧アニメーション効果を比較します。

関連セッション

コメント

GitHub Issues · utterances