WWDC Quick Look 💓 By SwiftGGTeam
Meet SwiftUI for spatial computing

Meet SwiftUI for spatial computing

元の動画を見る

ハイライト

SwiftUI は、visionOS アプリケーションを構築するための最良の方法であり、3 つのシーン タイプ (Window、ボリューム、ImmersiveSpace)、新しい 3D 機能 (Model3D、RealityView、3D ジェスチャ)、およびボタンのホバー効果から TabView のサイド ナビゲーションまで、追加構成なしでプラットフォームに自動的に適応するスマート デフォルトを提供します。

主な内容

Apple は、SwiftUI を使用例して、visionOS のシステム インターフェイス全体をゼロから構築しました。ホームビュー、コントロールセンター、Safari、Freeform の 3D 描画ボード、Keynote のイマーシブ リハーサルはすべて SwiftUI で実装されています。

(00:38) これは、SwiftUI の知識をこの新しいプラットフォームに直接転送できることを意味します。 Button、Toggle、TabView などの基本コントロールは使い慣れた API を保持していますが、プラットフォーム固有の外観と操作性を備えています。

3つのシーンタイプ

(03:49)visionOS アプリケーションは 3 つのシナリオで構成されます。

  • ウィンドウ: 環境が透けて見えるガラスの背景を持つ従来の 2D ウィンドウ。 Safari や Freeform などのコンテンツベースのアプリケーションに適しています。
  • ボリューム: 限られたスペースに 3D オブジェクトを表示するための新しい 3D ウィンドウ スタイル。他のアプリケーションと並べて表示できます。 3D モデルのプレビュー、ボードゲームに適しています。
  • ImmersiveSpace (フルスペース): 完全に没入型の 3D 環境。周囲の環境を隠し、ユーザーをまったく新しい世界に連れて行くことができます。ゲームや没入型の体験に適しています。

これら 3 つのシナリオは混合することができます。ウィンドウからボリュームを開いて 3D プレビューを表示し、ボリュームからフル スペースに入ると完全に没入できます。

プラットフォーム適応のためのインテリジェントなデフォルト値

(02:02) ボタンは、デフォルトで境界線付きのスタイルと鮮やかなマテリアルの背景を使用例し、視線によるホバー効果をサポートし、押すとスケールし、音声フィードバックを提供します。ナビゲーション バーのボタンは、ユーザーがボタンを見ると自動的にツールチップを表示します。

TabView はウィンドウの左端からぶら下がっており、見ると展開してラベル テキストが表示されます。これは、プラットフォームの「視線はインタラクション」機能を最大限に活用しています。

3D 機能

(14:10) Model3DImage と同じくらい単純で、3D モデルを非同期にロードして表示します。 RealityView は、RealityKit の完全な機能を提供し、カスタム マテリアル、物理学、およびアニメーションをサポートします。 SpatialTapGestureRotateGesture3D などの 3D ジェスチャにより、インタラクションがより自然になります。

##詳細

アプリケーション ポータル (アプリ プロトコル)

(03:37)visionOS アプリケーションは、他のプラットフォームと同じ App プロトコルを使用例します。

@main
struct WorldApp: App {
    var body: some Scene {
        WindowGroup("Hello, world") {
            ContentView()
        }
    }
}

キーポイント:

  • WindowGroup は、macOS/iPadOS と同じように複数のウィンドウを自動的にサポートします
  • アプリケーションは同時に複数のシーンを定義できます

TabView を備えたウィンドウ

(07:03) TabView を使用例してアプリのコンテンツを整理します。

@main
struct WorldApp: App {
    var body: some Scene {
        WindowGroup("Hello, world") {
            TabView {
                Modules()
                    .tag(Tabs.menu)
                    .tabItem {
                        Label("Experience", systemImage: "globe.americas")
                    }
                FunFactsTab()
                    .tag(Tabs.library)
                    .tabItem {
                        Label("Library", systemImage: "book")
                    }
            }
        }
    }
}

キーポイント:

  • TabView はウィンドウの左端に自動的に表示されます
  • 展開すると、ラベルのテキスト全体が表示されます。
  • .tag を使用例して、現在選択されているタグを識別します

素材とガラスの背景

(08:42) システム マテリアルを使用例して視覚的な階層を作成します。

VStack(alignment: .leading, spacing: 12) {
    Text("Stats")
        .font(.title)

    StatsGrid(stats: stats)
        .padding()
        .background(.regularMaterial, in: .rect(cornerRadius: 12))
}

キーポイント:

  • ウィンドウはデフォルトでガラスの背景を使用例しており、明モードと暗モードの区別はありません。
  • .regularMaterial ガラスに鮮やかな暗い背景を追加します
  • マテリアルは環境に自動的に適応し、コンテンツを読みやすく保ちます
  • .secondary 前景スタイルを使用例して視覚的な階層を表現します

カスタムボタンスタイル

(09:23) ホバー効果のあるカスタム ボタンを作成します。

struct FunFactButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .padding()
            .background(.regularMaterial, in: .rect(cornerRadius: 12))
            .hoverEffect()
            .scaleEffect(configuration.isPressed ? 0.95 : 1)
    }
}

// 使用例
Button(action: {
    // perform button action
}) {
    VStack(alignment: .leading, spacing: 12) {
        Text(fact.title)
            .font(.title2)
            .lineLimit(2)
        Text(fact.details)
            .font(.body)
            .lineLimit(4)
        Text("Learn more")
            .font(.caption)
            .foregroundStyle(.secondary)
    }
    .frame(width: 180, alignment: .leading)
}
.buttonStyle(.funFact)

キーポイント:

  • カスタム ButtonStyle を手動で追加する必要があります .hoverEffect()
  • 内蔵コントロールはホバー効果を自動的に取得します
  • .hoverEffect() 現在のコンテキストに適したエフェクトを自動的に選択します
  • ボタンの背景の形状に合わせた効果が得られます

ボリューム (3D ウィンドウ)

(14:17) 3D コンテンツを表示するボリュームを作成します。

@main
struct WorldApp: App {
    var body: some Scene {
        WindowGroup {
            Globe()
        }
        .windowStyle(.volumetric)
        .defaultSize(width: 600, height: 600, depth: 600)
    }
}

Model3D を使用例して 3D モデルを表示します。

import SwiftUI
import RealityKit

struct Globe: View {
    @State var rotation = Angle.zero
    
    var body: some View {
        ZStack(alignment: .bottom) {
            Model3D(named: "Earth")
                .rotation3DEffect(rotation, axis: .y)
                .onTapGesture {
                    withAnimation(.bouncy) {
                        rotation.degrees += Double.random(in: 360...720)
                    }
                }
                .padding3D(.front, 200)
            
            GlobeControls()
                .glassBackgroundEffect(in: .capsule)
        }
    }
}

キーポイント:

  • .windowStyle(.volumetric) ウィンドウをボリュームに変更
  • .defaultSize(width:height:depth:) 3D 寸法を設定する
  • Model3D は 3D モデルを非同期的にロードし、プレースホルダーを自動的に表示します
  • rotation3DEffect 3D 空間で回転する
  • .padding3D(.front, 200) は Z 軸方向に間隔を追加します
  • .glassBackgroundEffect コントロールにガラスの背景を追加します

RealityView と 3D ジェスチャ

(17:30) RealityView を使用例して RealityKit の全機能にアクセスします。

struct Earth: View {
    @State private var pinLocation: GlobeLocation?
    @State private var rotation = Angle.zero
    @State private var animatingRotation = false
    
    var body: some View {
        RealityView { content in
            if let earth = try? await ModelEntity(named: "Earth") {
                earth.addImageBasedLighting()
                content.add(earth)
            }
        } update: { content, attachments in
            if let pin = attachments.entity(for: "pin") {
                content.add(pin)
                placePin(pin, at: pinLocation)
            }
        } attachments: {
            if let pinLocation {
                GlobePin(pinLocation: pinLocation)
                    .tag("pin")
            }
        }
        .gesture(
            SpatialTapGesture()
                .targetedToAnyEntity()
                .onEnded { value in
                    withAnimation(.bouncy) {
                        rotation.degrees += Double.random(in: 360...720)
                        animatingRotation = true
                    } completion: {
                        animatingRotation = false
                    }
                    pinLocation = lookUpLocation(at: value)
                }
        )
    }
}

キーポイント:

  • RealityView のクロージャは async/await をサポートします
  • update クロージャは状態が変化すると呼び出され、RealityKit コンテンツの更新に使用例されます
  • attachments クロージャは SwiftUI ビューを RealityKit エンティティにアタッチします
  • SpatialTapGesture は完全な 3D クリック位置を提供します
  • .targetedToAnyEntity() クリックされたエンティティと相対位置を取得します
  • addImageBasedLighting() 画像ベースの照明を追加する

ImmersiveSpace (没入型空間)

(21:11) 完全に没入型のエクスペリエンスを作成します。

@main
struct WorldApp: App {
    var body: some Scene {
        // ウィンドウシーン
        WindowGroup {
            ContentView()
        }
        
        // 没入型空間
        ImmersiveSpace(id: "solar-system") {
            SolarSystem()
        }
        .immersionStyle(selection: .constant(.full), in: .full)
    }
}

struct ContentView: View {
    @Environment(\.openImmersiveSpace) private var openImmersiveSpace
    @Environment(\.dismissImmersiveSpace) private var dismissImmersiveSpace
    
    var body: some View {
        Button("View Outer Space") {
            Task {
                await openImmersiveSpace(id: "solar-system")
            }
        }
    }
}

struct SolarSystem: View {
    var body: some View {
        Earth()
        Sun()
        Starfield()
    }
}

struct Starfield: View {
    var body: some View {
        RealityView { content in
            let starfield = await loadStarfield()
            content.add(starfield)
        }
    }
}

キーポイント:

  • ImmersiveSpace には一意の id が必要です
  • openImmersiveSpace 環境値はスペースを開くために使用例されます
  • dismissImmersiveSpace はスペースを閉じるために使用例されます
  • .immersionStyle(selection:in:) 没入スタイルを設定する
  • .full 周囲を完全に隠します
  • .mixed 仮想コンテンツと現実を共存させる
  • .progressive ユーザーはデジタル クラウンを通じて没入レベルを調整できます

イマージョンスタイルの切り替え

(22:50) 複数の没入スタイルをサポートし、動的に切り替えます。

@main
struct WorldApp: App {
    @State private var selectedStyle: ImmersionStyle = .full
    
    var body: some Scene {
        WindowGroup {
            ContentView(selectedStyle: $selectedStyle)
        }
        
        ImmersiveSpace(id: "solar-system") {
            SolarSystem()
        }
        .immersionStyle(selection: $selectedStyle, in: .full, .mixed, .progressive)
    }
}

キーポイント:

  • immersionStyleselection バインディングにより、ランタイム切り替えが可能になります
  • in パラメータはサポートされているスタイルをリストします
  • ユーザーは、.progressive スタイルで Digital Crown を介して没入感を調整できます

重要なポイント

1.電子商取引アプリケーションに 3D 製品プレビューを追加

やること: ユーザーが製品の 3D モデルをさまざまな角度から表示できるように、製品詳細ページにボリュームを追加します。

費用対効果: 3D プレビューにより、ユーザーは購入前に製品の外観をより明確に理解できるため、返品率が削減されます。ユーザーのショッピング プロセスを中断することなく、ボリュームを他のアプリケーションと並べて表示できます。

ハジメ: Model3D を使用例して製品の USDZ モデルをロードし、.windowStyle(.volumetric) を使用例してボリュームを作成し、rotation3DEffectonTapGesture を追加してユーザーが回転および対話できるようにします。

2.没入型の教育アプリケーションを構築

やること: 天文学または生物学の教育アプリケーションを作成し、ウィンドウにナレッジ ポイントを表示し、ボタンを通じてフル スペースの没入型エクスペリエンスに入り、学生が太陽系や細胞構造を没入的に探索できるようにします。

価値: 空間コンピューティングにより、抽象的な概念が直感的に理解できるようになります。学生たちは太陽系に「入って」、惑星の動きをあらゆる角度から観察することができ、これは 2D スクリーンでは提供できない体験です。

ハジメ: WindowGroup を使用例してコース ナビゲーション インターフェイスを構築し、ImmersiveSpace を使用例して没入型シーンを作成し、openImmersiveSpace を使用例して入場します。 RealityView を使用例して複雑な 3D モデルをロードし、SpatialTapGesture を追加してインタラクティブな注釈を実装します。

3. 3D モデル ビューアをデザイン ツールに追加

やること: Volume をグラフィック デザインまたは建築デザイン アプリケーションに統合すると、ユーザーは 2D ウィンドウでデザインの編集を続けながら、インポートされた 3D モデルをプレビューできるようになります。

コスト: 設計者は 2D 図面と 3D モデルの両方を表示する必要があります。ボリュームは他のアプリケーション ウィンドウと並べて表示でき、真のマルチタスク ワークフローをサポートします。

ハジメ: RealityView を使用例してモデルをロードし、attachments を使用例して SwiftUI コントロール (寸法設定やマテリアル選択パネルなど) を 3D シーンにオーバーレイします。 glassBackgroundEffect を使用例して、コントロールをプラットフォームの視覚スタイルにブレンドします。

4.視線認識を実現するインタラクティブなインターフェース

やること: プラットフォームの「視線はインタラクション」機能を利用して、インターフェース要素はユーザーの視線位置に基づいて、より多くの情報を自動的に展開したり、操作プロンプトを表示したりできます。

値: これは、空間コンピューティング プラットフォームの独自のインタラクション方法です。 TabView の視線拡張はその良い例であり、同じパターンを情報密度の高いインターフェイスに適用できます。

ハジメ: SwiftUI の組み込みコントロール (視線操作を自動的にサポートします) を使用例して、カスタム コントロールに .hoverEffect() を追加します。 ornament モディファイアと組み合わせると、補助情報をウィンドウの端に配置し、詳細を確認する際に拡大します。

関連セッション

コメント

GitHub Issues · utterances