WWDC26 SwiftUIの新機能のまとめ

2026年6月9日
WWDC / Swift / SwiftUI / iOS / macOS / iPadOS / watchOS /

WWDC26のセッション SwiftUIの新機能 で紹介されたSwiftUIの主要なアップデートをまとめます。

SwiftUIの新機能

0. はじめにまとめ

ざっくりとは以下のアップデートがあります。

1. Refreshed look and feel(洗練された外観と操作感)

Liquid Glassデザインの自動更新

iOS 27 / macOS 17 / iPadOS 27 向けにXcode 27でリビルドするだけで、アプリに刷新された Liquid Glassデザイン が自動的に適用されます。コードを1行も変更することなく、最新のUI外観を得ることができます。

liquid_glass_slider_tint

Liquid Glassは洗練された外観を持ち、新しいLiquid Glassスライダーに自動的に対応してティントを調整します。

macos_interactive_hover

macOSでは、Liquid Glassのカスタム要素を「インタラクティブ」としてマークすると、ユーザーのクリックにより流動的に反応します。これはマウスポインターでうまく動作するように最適化されています。

ipad_inactive_window

ウィンドウの制御とアクティブ状態の検知

iPadやMacでは、非アクティブなウィンドウは独特の外観になり、アイコンとテキストが自動的に薄くなってアクティブなウィンドウが区別しやすくなります。

ipad_files_app_switch

この動作を細かく制御するために、新しい環境変数 appearsActive が導入されました。ウィンドウが非アクティブのときに、サイドバーのカスタムアカウントボタンなどの不透明度を下げるカスタマイズが可能です。

struct SidebarFooterView: View {
    @Environment(\.appearsActive) private var appearsActive

    var body: some View {
        MyAccountView()
            .opacity(appearsActive ? 1 : 0.5)
    }
}
appearsActive

メニューバーのカスタマイズ

iPadとMacのメニューバーには、デフォルトで最小限のアイコンが表示され、主要なアクション専用になります。しかし、labelStyle(.titleAndIcon) モディファイアを適用することで、特定のメニュー項目にアイコンを表示して目立たせることができます。

CommandMenu("Stickers") {
    Button { openStore() } label: {
        Label("Store", systemImage: "bag.fill")
            .labelStyle(.titleAndIcon)
        }
    }
    // Other menu items
}

store_menu_item_icon

iPhoneアプリのサイズ変更対応

iOS 27では、iPhoneアプリもサイズ変更可能(リサイズ対応)になります。

Xcode 27のライブプレビューにリサイズハンドルが追加され、iPhone Mirroringの動作や、iPadでiPhoneアプリとして実行された場合のレイアウト変更(サイズクラスの対応)を即座にプレビュー・テストできるようになりました。

ipad_iphone_app_resize

UIKitとSwiftUIが混在するアプリの注意点

画面のジオメトリを正しく決定する方法、ビューのサイズ設定にidiomではなくサイズクラスを使用すること、インターフェースの向きの変更への対応などを考慮する必要があります。詳細は「Modernize your UIKit app」セッションを参照してください。

Modernize your UIKit app

Tab(role: .prominent)による特別なタブ配置

TabView 内の特定のタブを目立たせるために、新しい prominent タブロールが追加されました。このロールを指定したタブ(例:ショッピングカートなど)は、画面の端に配置され、他の通常のタブと区別されます。

TabView {
    Tab { EventsTab() }
    Tab { HolidaysTab() }
    Tab { FunTab() }

    Tab(role: .prominent) {
        CartTab()
    }
}

prominent_tab_concept

ツールバーAPIの強化

アプリウィンドウやスクリーンのサイズを変更した際、ツールバー内の項目はシステムによって自動的に調整されます。収まりきらない項目は自動的に非表示になり、オーバーフローメニューに隠れます。

スペース制限がある状況でも、重要なアクションを常に表示させたり、特定の配置を維持するための新しいツールバーAPIが追加されました。

StickerPageView()
    .toolbar {
        ToolbarItemGroup {
            UndoButton()
            RedoButton()
        }
        .visibilityPriority(.high)
        ToolbarOverflowMenu {
            ChoosePhotoButton()
            ExportAsImageButton()
            ClearAllStickersButton()
        }
        ToolbarItem(placement: .topBarPinnedTrailing) {
            ShareButton()
        }
    }

perfectly_configured_toolbar

スクロール時のツールバー最小化

スクロール中の作業スペースを最大化するために、新しい toolbarMinimizeBehavior モディファイアが追加されました。onScrollDown を指定することで、下方向へスクロールした際に自動的にナビゲーションバーを移動(最小化)させることができます。

ScrollView {
    StickerListView()
}
.toolbarMinimizeBehavior(.onScrollDown, for: .navigationBar)

toolbar_minimized_on_scroll

toolbarMinimizeBehavior

2. Document-based apps(ドキュメントベースのアプリ)

SwiftUIは、ドキュメントベースのアプリを構築するための基盤(FileDocumentReferenceFileDocument プロトコル)をさらに拡張しました。

新規作成(Cmd+N)や開く(Cmd+O)、自動保存、スマートな編集済みインジケーターなどの標準システム機能をそのまま活用しながら、以下の3つの主要な改善点が追加されました。

  1. ドキュメント作成コンテキスト(Document Creation Context)の追加
  2. ディスクの読み書きパフォーマンスの最適化
  3. ドキュメントURLへのダイレクトアクセス対応

document_api_improvements

ドキュメント作成コンテキスト(DocumentCreationSource)

新規ドキュメントを作成する際のソース(空白から作成、写真から作成など)を宣言するための DocumentCreationSource APIが追加されました。

@main
struct Stickers: App {
    var body: some Scene {
        DocumentGroupLaunchScene("Create a Sticker Page") {
            NewDocumentButton("New Sticker Page", source: .blank)
            NewDocumentButton("Sticker Page from Photo…", source: .photo)
        }
       
        DocumentGroup { document in
            StickerPageDocumentView(document)
        } { configuration, context in
            StickerPageDocument(configuration: configuration, context: context)
        }
    }
}
  
extension DocumentCreationSource {
    static let blank = Self(id: "blank")
    static let photo = Self(id: "photo")
}

ボタンが選択されると、SwiftUIはソース情報を context パラメーターを通じてドキュメントの作成クロージャに渡します。初期化時に context を確認し、例えば photo がソースの場合は、ドキュメントが開いた直後にフォトピッカーを表示するようなフローを構築できます。

photopicker_open_on_creation sticker_page_created_from_photo

読み書きパフォーマンスの最適化(Observationとの連携)

DocumentGroup 宣言と @Observable マクロがシームレスに連携するようになり、ビューは依存しているプロパティが変更されたときのみ更新されるようになります。

@main
struct Stickers: App {
    var body: some Scene {
        DocumentGroup { /* ... */ }
        WindowGroup { /* ... */ }
    }
}

observation_framework_integration view_update_on_dependency_change

WritableDocumentとDocumentWriterによる書き込み最適化

効率的な書き込みを実現するために、ドキュメントクラスを WritableDocument に準拠させ、DocumentWriter を提供します。

@Observable
final class StickerDocument: WritableDocument {
    
    static let writableDocumentTypes: [UTType] = [.stickerDocument]
  
    @MainActor
    func snapshot(contentType: UTType) async throws -> sending PageSnapshot {
        makeSnapshot()
    }
      
    func writer(configuration: sending WriteConfiguration) -> sending Writer {
        Writer(contentType: configuration.contentType)
    }
}

snapshot メソッドは、現在のドキュメント状態を表す値型スナップショット(例:背景画像やステッカー位置をカプセル化した PageSnapshot)を返します。

struct PageSnapshot {
    var background: Image
    var metadata: StickerPlacements
    var stickers: [Image]
}

struct StickerPlacements { /* ... */ }

DocumentWriter に準拠した Writer 構造体が、非同期にディスクへデータを書き込みます。

struct Writer<Snapshot>: DocumentWriter {
    typealias Snapshot = PageSnapshot
  
    let contentType: UTType
    
    nonisolated func write(
        snapshot: sending PageSnapshot, to destination: URL,
        previous: sending PageSnapshot?, progress: consuming Subprogress
    ) async throws {
        // write .stickerDocument
    }
}
パフォーマンス最適化のメリット:

ReadableDocumentによる読み込み

WritableDocument に対応する対として、読み込み側には ReadableDocument プロトコルと DocumentReader が提供され、重いディスク読み込み処理をバックグラウンドで処理できます。

snapshot_writer_reader_concept

複数フォーマットでの保存サポート

同じドキュメントを、独自のパッケージ形式だけでなくPNG画像など他のフォーマットでもエクスポートできるように、writableContentTypes に複数のUTTypeを登録できます。

@Observable
final class StickerDocument: WritableDocument {
  
    static let writableContentTypes: [UTType] = [.stickerDocument, .png]
}

Writerwrite メソッド内で、リクエストされた contentType に応じて書き出し処理を分岐します。

struct Writer<Snapshot>: DocumentWriter {
    typealias Snapshot = PageSnapshot
  
    let contentType: UTType
    
    nonisolated func write(
        snapshot: sending PageSnapshot, to destination: URL,
        previous: sending PageSnapshot?, progress: consuming Subprogress
    ) async throws {
        if contentType.conforms(to: .stickerDocument) {
            // write .stickerDocument
        } else if contentType.conforms(to: .png) {
            let context = CGContext(/* ... */) 
            context.draw(/* ... */)
        }
    }
}

pirate_sticker_doc_export

DocumentCreationSource

3. Presentation and interaction(プレゼンテーションとインタラクション)

reorderableコンテナAPIによる要素の並び替え

ListやGridなど、あらゆるコンテナ内の要素をドラッグ&ドロップで並び替え可能にする新しい reorderable APIが追加されました。

ForEach 内のビューに .reorderable() を適用し、親コンテナに .reorderContainer(for:) を設定します。

List {
    ForEach(stickers) { sticker in
        StickerListItemView(sticker: sticker)
    }
    .reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
    difference.apply(to: &stickers)
}

並び替え処理には、Apple公式のオープンソースパッケージである OrderedCollections (swift-collections) の apply メソッドを利用するのがベストプラクティスです。

import OrderedCollections // from https://github.com/apple/swift-collections

extension ReorderDifference where CollectionID == ReorderableSingleCollectionIdentifier {
    func apply(to values: inout [some Identifiable<ItemID>]) {
        var dictionary = OrderedDictionary(uniqueKeys: values.map { $0.id }, values: values)
        let destinationOffset: Int? = switch destination.position {
        case .before(let destination):
            dictionary.keys.firstIndex(of: destination)
        case .end:
            nil
        }
        dictionary.move(keys: sources, to: destinationOffset ?? values.endIndex)
        values = dictionary.values.elements
    }
}

LazyVGridでの並び替え

このAPIは List 以外のあらゆるコンテナに対応しており、以下のコードのように List から LazyVGrid に置き換えても、内部ロジックを変更することなく並び替え動作が実現します。

LazyVGrid {
    ForEach(stickers) { sticker in
        StickerListItemView(sticker: sticker)
    }
    .reorderable()
}
.reorderContainer(for: Sticker.self) { difference in
    difference.apply(to: &stickers)
}

このアップデートにより、watchOSでも初めて並び替え機能がサポートされました。

drag_and_drop_session_info

Code-along: Build powerful drag and drop in SwiftUI

あらゆるビューでのスワイプアクション対応

これまで List のみで利用可能だった .swipeActions モディファイアが、LazyVStack やその他のコンテナ内のビューでも使用できるようになりました。スクロールビュー内の項目をカスタマイズする柔軟性が大幅に向上します。

スワイプを実行したいコンテナの親ビューに .swipeActionsContainer() を追加して使用します。

ScrollView {
    LazyVStack {
        ForEach(stickers) { sticker in
            StickerListItemView(sticker: sticker)
                .swipeActions {
                    DeleteButton(sticker: sticker)
                }
        }
    }
}
.swipeActionsContainer()

swipe_actions_custom_demo

swipeActionsContainer

confirmationDialog と alert の item バインディング対応

確認ダイアログ(confirmationDialog)および alert が、シート(sheet)と同様の item-binding パターン(値が代入されたときに自動でプレゼンテーションを表示する仕組み)をサポートしました。

これにより、削除ボタンなどを押して状態変数に値(アイテム)が格納された時点でダイアログを表示することが可能です。

struct StickerCanvasView: View {
    var stickers: [Sticker]
    @State private var stickerToDelete: Sticker?

    var body: some View {
        ZStack {
            ForEach(stickers) { sticker in
                PlacedStickerView(sticker: sticker)
                    .contextMenu {
                        // ...
                    }
            }
        }
        .confirmationDialog(
            "Delete?", item: $stickerToDelete
        ) { sticker in
            DeleteStickerButton(sticker)
        }   
    }
}

alert でも同様に動作します。

        .alert(
            "Delete?", item: $stickerToDelete
        ) { sticker in
            DeleteStickerButton(sticker)
        }   

4. Data flow and performance(データフローとパフォーマンス)

AsyncImageのHTTPキャッシングの標準サポート

これまで AsyncImage は画像をメモリに保持していなかったため、画面外にスクロールして戻ると再読み込みが発生していました。

iOS 27 / macOS 17 以降では、AsyncImage が標準の HTTPキャッシュ機能 を自動的にサポートし、キャッシュヘッダーを尊重してデータをキャッシュするようになりました。コードの変更は不要で、すべてのアプリで自動的に有効化されます。

ダウンロードのカスタマイズ

キャッシュポリシーを個別に設定する場合や、長期のカスタムキャッシュを使用する場合は、独自の URLRequest やカスタム URLSession を定義して asyncImageURLSession モディファイアに渡すことができます。

@Observable class StickerStore {
    static let imageSession: URLSession = {
        let config = URLSessionConfiguration.default
        config.urlCache = URLCache(
            memoryCapacity: 64 * 1024 * 1024,
            diskCapacity: 256 * 1024 * 1024)
        return URLSession(configuration: config)
    }()
}

ForEach(pets) { pet in
    AsyncImage(request: URLRequest(
        url: pet.imageURL,
        cachePolicy: .returnCacheDataElseLoad)
    )
}
.asyncImageURLSession(StickerStore.imageSession)
asyncImageURLSession

@Observableクラスの @State初期化がlazyに

親ビューの再レンダリングによってビューの構造体が再作成される際、従来の挙動では @State 内に定義された @Observable クラスインスタンスもその都度新しく初期化され(その後破棄される)、不要なパフォーマンスオーバーヘッドとなっていました。

Xcode27以降では、@State プロパティでインスタンス化されるクラスが自動的に lazy(遅延初期化) となり、ビューのライフタイム中に一度だけ初期化されるように最適化されました。

@Observable class StickerStore { }

struct StickerStoreView: View {
    // store is now lazily initialized, only
    // created once for the lifetime of the view
    @State private var store = StickerStore()

    var body: some View {
        // ...
    }
}

state_lazy_initialization_concept

この挙動変更は、@Observable が導入された最初のOSバージョン(iOS 17、macOS 14 等)まで自動的に バックポート適用 されます。

state_lazy_backport_os

コンパイラ型チェックの高速化と ContentBuilder

ビューが深くネストされた構造(SectionGroupForEach の入れ子)では、コンパイラがどのオーバーロード候補(ビュービルダーかテーブル行ビルダーかなど)を使用すべきか判断するために、膨大な型チェックの組み合わせパスを検証する必要があり、ビルド時に「型チェックの時間が長すぎます」というコンパイルエラーが発生することがありました。

type_checking_single_path

iOS 27 / macOS 17 以降では、最も一般的な複数のビルダー型が内部的に統合され、コンパイラが処理すべきパスが1つに削減されました。

この型安全な統合を担うのが、新しく導入された @ContentBuilder です。

@ContentBuilder
func stickerLibraryView() -> some View {
  // ...
}

ContentBuilder は既存の ViewBuilder の進化形で、iOSの最小デプロイターゲットに関わらず使用でき、Xcode 27 を使用したSwiftUIの型チェックのビルドパフォーマンスを大幅に向上させます。

ContentBuilder

Xcode 27 エージェントスキル(Agent Skills)

agent_skills_export_command

開発環境の支援機能として、Xcode 27 の Coding Assistant 向けに新しい2つのエージェントスキルが導入されました。

これらのスキルは、以下のコマンドを実行することでMarkdownファイルとしてエクスポートでき、サードパーティ製の他の開発ツールやワークフローに組み込むことも可能です。

xcrun agent skills export

6. まとめ

Related Entries
Latest Entries