View in English

  • メニューを開く メニューを閉じる
  • Apple Developer
検索
検索を終了
  • Apple Developer
  • ニュース
  • 見つける
  • デザイン
  • 開発
  • 配信
  • サポート
  • アカウント
次の内容に検索結果を絞り込む

クイックリンク

5 クイックリンク

ビデオ

メニューを開く メニューを閉じる
  • コレクション
  • トピック
  • すべてのビデオ
  • 利用方法

WWDC25に戻る

  • 概要
  • トランスクリプト
  • コード
  • SwiftUIの空間レイアウトの紹介

    SwiftUIを使用して空間体験を構築するための新しいツールを紹介します。visionOSにおける3D SwiftUIビューの基本、奥行き/深度のアライメントを使って既存のレイアウトをカスタマイズする方法、モディファイアを使用して空間内にビューを配置したり、回転したりする方法を学びましょう。空間コンテナを使って、同じ3D空間内に複数のビューを整列させ、イマーシブで魅力的なアプリを作成する方法についても解説します。

    関連する章

    • 0:00 - イントロダクション
    • 2:47 - 3Dビュー
    • 7:18 - 奥行き/深度のアライメント
    • 11:41 - 回転レイアウト
    • 16:28 - 空間コンテナ
    • 19:22 - 次のステップ

    リソース

    • Canyon Crosser: Building a volumetric hike-planning app
    • Human Interface Guidelines: Designing for visionOS
      • HDビデオ
      • SDビデオ

    関連ビデオ

    WWDC25

    • SwiftUIとRealityKitの連係

    WWDC22

    • SwiftUIによるカスタムレイアウトの作成

    WWDC19

    • SwiftUIでカスタムビューを構築する
  • このビデオを検索

    「SwiftUIの空間レイアウトについて」へ ようこそ 私はSwiftUIチームのエンジニア Trevorです このセッションでは SwiftUIを使って 楽しい空間体験を構築するための 手法をご紹介します

    私はBOT-anistという お気に入りのアプリを拡張して SwiftUIの新しい空間レイアウト機能を 実現しました

    このアプリでは 様々な構成要素 色 素材を使って 楽しいロボットを カスタマイズできます 新しく作成したロボットを使って 自分の仮想庭園を手入れできます この小さなロボットを作るのが大好きです 最近 私の作品をカタログ化するための 新しいビューの開発に 取り組んでいます

    ロボットをカスタマイズできるだけでなく 保存して様々なロボットを 収集できるのです

    ロボットをブラウズするための 新しい3Dシーンをお見せします これらの体験はすべて SwiftUIを使って作りました

    visionOSで3D体験を構築していれば RealityKitを 使ったかもしれません

    RealityKitは 3Dアプリを構築するための 素晴らしいフレームワークで 特に 物理シミュレーションのような 複雑な動作を得意とします

    SwiftUIを使用してきた人なら 使い慣れた宣言型構文を使っても ビルドできます また RealityKitの機能をアプリの 一部でのみ使う場合もあるでしょう visionOS 26では SwiftUIの既存の2Dレイアウトツールや アイデアを使って 3Dアプリを構築できます

    SwiftUIレイアウトを使えば アニメーション サイズ変更 状態管理のサポートが 組み込まれているため カルーセルからロボットを 削除すると SwiftUIによって他のすべての ロボットの位置やサイズが空間のサイズに 対応してアニメーション化されます

    ボリュームのサイズを変更すると カルーセルと 中のロボットのサイズが 自動的に変更されます ロボットレイアウトの構築に使用した 新しいツールについて説明する前に

    まず SwiftUIレイアウトシステムの この3D拡張機能は 既存の2Dレイアウト概念に基づいています

    初めてSwiftUIレイアウトを扱う方は 「Building custom views with SwiftUI」と 「Compose custom layouts with SwiftUI」 を先にご確認ください このビデオでは visionOSにおける 3D SwiftUIビューの基本 奥行きアライメントを使って既存の レイアウトをカスタマイズする方法 レイアウトシステム内でビューを回転させる 新しいモディファイアであるrotation3DLayout 同じ3D空間内に複数のビューを 整列させる機能である SpatialContainerとspatialOverlayについて 説明します

    ビューとレイアウトシステムについて 説明しましょう

    SwiftUIはアプリのビューごとに 幅 高さ X位置とY位置を計算します

    サイズを変更できない画像などの 一部のビューにはアセットのサイズと一致する 固定フレームが使用されます

    このColorのような一部のビューには 親から提供された すべての空間を占有する 柔軟なフレームが使用されます

    レイアウトによって子が 最終フレームに編成されます 黄色で示されているVStackのフレームは 利用可能な空間と 含まれる子により決まります この例では 高さは中にある2つの 画像ビューの合計になっています visionOSも動作は同じですが ビューが3Dである点が異なります レイアウトシステムの動作は同じでありながら 2次元ではなく 3次元に 応用されているわけです

    つまり 各ビューの 幅と高さに加えて SwiftUIでは奥行きとZ位置も 計算されます iOSで2Dフレームを視覚化するのに borderモディファイアをよく使いますが

    visionOSでは 3Dフレームを 視覚化するために独自の debugBorder3Dモディファイアを作成しました このモディファイアの作成方法については このセッションで習う APIを使ってこのビデオの最後に 説明します

    debugBorder3Dでも Model3Dの動作が 画像と同じであることがわかりますが 2次元ではなく3次元であり 幅 高さ 奥行きが 固定されています ビューはすべて3Dですが 奥行きがゼロになる場合もあります

    画像 色 テキストなど 平面体験の構築に使用する ビューの多くは 奥行きがゼロとなるため 動作がiOSと同じになります

    Colorがデフォルトで提案された 利用可能なすべての幅や高さを 占有するように 一部のビューには 柔軟な奥行きが使われます visionOSでは RealityViewのような特定のビューが デフォルトで利用可能な すべての奥行きを占有します

    GeometryReader3Dには 同様の柔軟なサイズ調整動作があり

    サイズ変更可能なモディファイアが 適用されたModel3Dと同様に動作します これにより ロボットが ウィンドウの幅に合わせて 引き伸ばされています このアスペクト比では 顔が少し長く見えるので 利用可能な空間に合わせて 拡大縮小しつつ 元の比率に 戻したいと思います

    resizable()に加えて 新しいscaledToFit3D モディファイアを使うことで モデルのアスペクト比を 維持しながら 利用可能な幅 高さ 奥行きに合わせて ロボットのサイズを拡大または縮小できます

    では 利用可能な奥行きは どう決まるのでしょうか ウィンドウのコンテンツは 幅や高さと同じように ルート奥行きの提案を受け取ります 幅や高さとは異なり サイズ変更可能ですが 奥行きの提案はウィンドウで固定されています この範囲を超えるとコンテンツが 一部表示されない場合があります

    同様に ボリュームでもコンテンツの幅 高さ 奥行きが提案されますが 深さもサイズ変更可能です ヒューマンインターフェイスガイドラインの 「Designing for visionOS」で ボリュームまたはウィンドウの 使い分けについてご確認ください

    一部のビューでは 含まれているビューの 奥行きの提案内容を変更できます VStackでサブビューの高さが 構成されるように ZStackでは奥行きが構成されます このZStackの奥行きは 前後に並ぶ両方のロボットが 納まるために必要な奥行きです

    VStackが利用可能な空間 子の数 子のタイプといった 要素に基づいて サブビューに様々な高さを 提案するのと同様に ZStackも同じ要素に基づいて 子に様々な奥行きを 提案する場合があります ここでは RealityViewがZStackで ロボットを前方に押し出し シーンで利用可能な奥行きを すべて使います

    既存のレイアウトタイプとスタックは visionOSの3Dであるため 理にかなったデフォルト動作が 奥行きにいくつか適用されます この例では HStackが親からの 奥行きの提案を受け 2つのモデルをぴったりと 中に納めるために 独自の奥行きを確立します

    HStackでは これら2つのロボットの 背面がデフォルトで並べられます

    この概念を奥行きアライメントと呼びます 奥行きアライメントは 3Dビューと奥行きに効果的に対応するため 既存のSwiftUIレイアウトタイプを カスタマイズできる新しいツールです 垂直方向や水平方向のアラインメントに 取り組んだことがあれば なじみがある概念でしょう それぞれの名前と説明とともに お気に入りのロボットを表示できる新しい ボリュメトリックウインドウを作成します 再利用しやすくするため まずロボットのModel3Dコードを 更新しましょう

    納まるように拡大縮小できる Model3Dから始めます

    ロボットのモデルをプリロードできる 新しいModel3DAssetタイプを 使用するよう リファクタリングします アプリ全体で使用できる 新しいResizableRobotViewに これらすべてを含めます ここではdebugBorder3Dも削除しておきます

    VStackを使用してResizableRobotViewと ロボットの詳細が記載された RobotNameCardが含まれる RobotProfileを作成します

    ここで問題があります

    このカードはVStackの後ろに 配置されているため 読みにくく ロボットモデルの影に 少し隠れてしまっています

    HStackでコンテンツを 中央 上 下端に配置する 設定ができるように visionOSでビューの奥行きでの 配置方法を設定するとよいでしょう

    デフォルトでは スタックとレイアウトタイプは 背面の奥行きアライメントを使用しています visionOS 26では任意のレイアウトタイプの DepthAlignmentを カスタマイズできます

    VStackLayoutを使用するように RobotProfileを更新して

    depthAlignmentモディファイアを 適用できる状態にし .frontアライメントを指定します

    .centerや.backのガイドを 使うこともできますが

    ロボットカードを読みやすくするには .frontが適切だと思います

    これでZapper Ironheartという名前と このロボットに関する 詳しい知識がしっかりと 頭に入りました

    前面 背面 中央といった 標準の構成が必要な場合 前面 背面 中央の奥行きアライメントを 使うと便利ですが より複雑な動作が必要な場合は どうすればよいでしょうか

    HStackで3つのロボットプロファイル ビューを設定し お気に入りの3体のロボットを表示する ボリュームを作成しています Greg-gear Mendelが一番のお気に入りなので このビューでほかの2体よりも 少し目立つように 設定したいと思います

    depthPodiumを使って お気に入りのロボットほど 近くに配置する方法を 使おうと思っています ロボット1 2 3の順に 近くに配置していきます

    上から奥行きを見た場合 最初のロボットの背面が 2番目のロボットの中央 そして3番目のロボットの前面と 揃っている状態です これにはカスタム奥行きアライメントが 必要になります

    まず DepthAlignmentIDプロトコルに 準拠する新しい構造体を定義します

    このアライメントの デフォルト値として 1つの要件を実装します

    DepthPodiumAlignmentのデフォルトとして 前面のアライメントガイドを使用します

    次に 新しいDepthAlignmentIDを使う 静的な定数を 奥行きアライメントに定義します

    これで各ロボットを含むHStackの 奥行きアライメントとして このdepthPodiumアライメントガイドを 使用できるようになりました

    これにより このガイドで指定した デフォルト値によって すべてのロボットの前面が 揃えられます

    次に 最後のロボットのdepthPodium アライメントガイドの 奥行きが ガイドの中央になるよう カスタマイズします

    中央のロボットを変更させ 背面が depthPodiumガイドに揃うようにします

    先頭のロボットは引き続き このアライメントのデフォルトである 前面のガイドを使用します

    シミュレータで見てみましょう

    奥行きを使ってずらして配置したことで Greg-gear Mendelが一番のお気に入りだと 一目でわかるようになりました

    奥行きアライメントは 既存のレイアウト内で 奥行きの位置を微調整する場合に 最適です では さらに奥行きを重視した構築は どうすればよいでしょうか より高度な3Dユースケースに最適な ツールが回転レイアウトです ビューに視覚効果を適用し 特定の軸を 中心にビューを回転させる 既存のrotation3DEffectモディファイアは おなじみかもしれません

    基本的な回転操作に最適な モディファイアです

    しかし HStackでモデルに 説明カードを付けて配置し Z軸に沿ってロケットを 90度回転させると ロケットがカードにぶつかって ボリュームから突き出てしまいます

    デバッグワイヤーフレームを回転効果の 前後に適用すると 何が起こっているのかを 理解しやすくなります 実線の赤色のワイヤーフレームは 効果によって回転していますが レイアウトシステムがロケットの ジオメトリの位置として認識しているのは 破線の青色のワイヤーフレーム部分です HStackでは青色のフレームを基準にサイズが 設定され コンテンツが配置されるため 回転と連動しません これは視覚効果がレイアウトに 影響を与えないためです つまり HStackでは rotation3DEffectを使用した際のロケットの 回転ジオメトリが認識されていません

    これは scaleEffectやオフセットを含む すべての視覚効果に

    当てはまります

    いずれの場合も レイアウトシステムでは モディファイアを基にビューのサイズや配置が 調整されることはありません これは周囲のフレームに 影響を与えることなく 1つのビューをアニメーション化する場合は 最適ですが

    影響を与えたい場合 回転ロケットをどう修正できるでしょうか

    朗報です visionOS 26では レイアウトシステム内の回転ビューの フレームを変更する 新しいrotation3DLayoutモディファイアが 導入されます ロケットモデルに適用するとHStackで サイズと配置が調整され ロケットと情報カードに十分なスペースが 割り当てられます

    rotation3DLayoutでは任意の角度と 軸での回転がサポートされるため 宇宙に向けて飛び立っているかのように ロケットを 45度回転させることができます

    rotation3DLayoutモディファイアの使用前後に デバッグワイヤーフレームを適用してみます 回転したロケットのフレームが 赤色で示されており 修正後のビューのフレームが 青色のワイヤーフレームで 示されています 青色のバウンディングボックスは 親と整合した軸であり 回転した赤色のフレーム内に ぴったりと納まっています

    では rotation3DLayoutを使って ビデオの冒頭でお見せした ロボットのカルーセルを構築する方法を 見ていきましょう

    「Compose custom layouts with SwiftUI」の RadialLayoutを使用します

    このカスタムレイアウトタイプでは ビューが円の中に配置されます 円周は利用可能な幅と 高さによって定義されます

    MyRadialLayoutはiOSに2Dビューを 配置するために記述されたものですが

    visionOSでもうまく機能します

    ペットの2D画像ではなくロボットの 3Dモデルを配置する場合でも

    ForEachを使って各ロボットの サイズ変更可能なModel3Dを カスタムレイアウト内に配置できます

    これでも問題ありませんが 垂直方向に並んでいます 私が目指すのはロボットをボリューム内で 水平方向に並べることです

    rotation3DLayoutを 放射状レイアウトに適用し X軸に沿ってビューを 90度回転させます 先ほどはカルーセルの高さだった要素で レイアウトシステム内の回転ビューの奥行きを 定義できます カルーセルは正しい 向きになりましたが ロボットは横たわっています

    X軸に沿って-90度回転させる2番目の rotation3DEffectを使えば ForEach内で各ロボットを逆回転させて 立たせることができます 横たわっていたドロイドたちが 立ち上がりました 最後に1点だけ修正します カルーセルはボリュームの高さに対し 中央揃えになっていますが カルーセルをボリュームのベースプレートと 同じ高さにしたいと思います

    debugBorder3Dを カルーセル全体に適用すると わかりやすくなります

    2Dレイアウトの場合と同じ戦略を 使用できます 上にスペーサを置いてVStack内で カルーセルを押し下げます ロボットをボリュームの下側に配置でき いい感じです 3Dレイアウトユーティリティベルトの もう1つのツールのペアを紹介しましょう それがSpatialContainerと spatialOverlayです ロボットのカルーセルにもう一つ 追加したい機能があります タップするとロボットを選択でき コントロールメニューと モデル下部にある リングが表示されて 選択されていることがわかります このリングもModel3Dとして 表されるため リングをロボットと同じ 3D空間に入れ込み 軸に沿って積み重ならないように 設定します モデルを同じ3D空間に配置する 新しいツールが必要です

    新しいSpatialContainer APIでは 入れ子人形のように 複数のビューを 同じ3D空間内に配置できます

    すべてのビューに3Dアライメントを 適用できます これがすべての子を.bottomFront アライメントガイドに従って並べた場合

    これが.topTrailingBackガイド に従って並べた場合です

    spatialOverlayも同様のツールで 別のビューと同じ3D空間にある 1つのビューを オーバーレイできます

    SpatialContainerと同様に 3Dアライメントがサポートされています

    並べるビューはロボットと 選択リングの2つだけです 私にとってロボットの ジオメトリが最も重要です ロボットのサイズに合わせて リングのサイズを変更でき 満足です ここでspatialOverlayを使用して 選択したロボットのビジュアルを実装します

    spatialOverlayモディファイアを ロボットモデルに追加します 選択済みとしてマークされたら サイズ変更可能なリングビューを コンテンツとして配置します .bottomアライメントを使って リングの底とロボットの底を 揃えます

    ロボットのカルーセルが いい感じになりました 既存の構成可能なSwiftUI APIを フル活用すればさらに簡単に改善できます

    debugBorder3Dモディファイアを実装して 全体の学習内容を 振り返ってみましょう

    これは先ほどModel3Dに適用して見せた モディファイアです

    Viewのextensionとして debugBorder3Dメソッドを定義します 変更したコンテンツに spatialOverlayを適用することで 適用先のビューと同じ 3D空間に 境界線をレンダリングします

    2D境界線 スペーサ 別の2D境界線が含まれる ZStackを組み込みます

    次に ZStack全体にrotation3DLayoutを 適用することで 先頭と末尾のビューに 境界線を配置します

    最後に この内部ZStackを 背面と前面に2D境界線がある 別のZStack内に 配置します これですべての端に境界線が付きました

    新しい3D APIを使って既存の 2D SwiftUIモディファイアを構成し まったく新しいものを作成できる点が 気に入っています

    2Dコンテキストで慣れ親しんでいる 多数のレイアウトツールや モディファイアに対応した 3Dアナログをご用意しています これらのAPIについては ドキュメントをご覧ください

    SwiftUIは 3Dアプリを構築するための 素晴らしいツールですが 依然として同じアプリ内で RealityKitとSwiftUIを 組み合わせて使う必要がある ユースケースも多く存在します

    SwiftUIのコンテンツが3Dになったことで RealityKitコードを扱う必要がある 場合もあります 友人のMaksとAmandaが両方の フレームワークを一緒に使用する BOTanistの優れた追加機能を 構築しました 詳しくは「Better Together: SwiftUI and RealityKit」をご覧ください 皆さんの3Dアプリを見るのが 楽しみで仕方ありません

    • 3:02 - Robot Image Frame

      // Some views have fixed frames
      
      Image("RobotHead")
        .border(.red)
    • 3:05 - Color Frame

      // Some views have flexible frames
      
      Color.blue
        .border(.red)
    • 3:15 - Layout Composed Frame

      // Layouts compose the frames of their children
      
      VStack {
        Image("RobotHead")
          .border(.red)
        Image("RobotHead")
          .border(.red)
      }
      .border(.yellow)
    • 4:00 - Model3D Frame

      // Some views have fixed depth
      
      Model3D(named: "Robot")
        .debugBorder3D(.red)
    • 4:25 - Zero Depth Views

      // Many views have 0 depth
      
      HStack {
        Image("RobotHead")
          .debugBorder3D(.red)
        Text("Hello! I'm a piece of text. I have 0 depth.")
          .debugBorder3D(.red)
        Color.blue
          .debugBorder3D(.red)
          .frame(width: 200, height: 200)
      }
    • 4:41 - RealityView Depth

      // RealityView takes up all available space including depth
      
      RealityView { content in
        // Setup RealityView content
      }
      .debugBorder3D(.red)
    • 4:56 - GeometryReader3D Depth

      // GeometryReader3D uses all available depth
      
      GeometryReader3D { proxy in
        // GeometryReader3D content
      }
      .debugBorder3D(.red)
    • 5:01 - Model3D scaledToFit3D

      // Scaling a Model3D to fit available space
      
      Model3D(url: robotURL) {aresolved in
        resolved.resizable()
      }aplaceholder: {
        ProgressView()
      }
      .scaledToFit3D()
      .debugBorder3D(.red)
    • 6:15 - ZStack depth

      // ZStack composes subview depths
      
      ZStack {
        Model3D(named: "LargeRobot")
          .debugBorder3D(.red)
        Model3D(named: "BabyBot")
          .debugBorder3D(.red)
      }
      .debugBorder3D(.yellow)
    • 6:33 - ZStack with RealityView

      // ZStack composes subview depths
      
      ZStack {
        RealityView { ... }
          .debugBorder3D(.red)
        Model3D(named: "BabyBot")
          .debugBorder3D(.red)
      }
      .debugBorder3D(.yellow)
    • 6:57 - Layouts are 3D

      // HStack also composes subview depths
      
      HStack {
        Model3D(named: "LargeRobot")
          .debugBorder3D(.red)
        Model3D(named: "BabyBot")
          .debugBorder3D(.red)
      }
      .debugBorder3D(.yellow)
    • 7:50 - ResizableRobotView

      struct ResizableRobotView: View {
        let asset: Model3DAsset
      
        var body: some View {
          Model3D(asset: asset) { resolved in
            resolved
              .resizable()
          }
          .scaledToFit3D()
        }
      }
    • 8:11 - Robot Profile 1

      //`Layout` types back align views by default
      
      struct RobotProfile: View {
        let robot: Robot
      
        var body: some View {
          VStack {
            ResizableRobotView(asset: robot.model3DAsset)
            RobotNameCard(robot: robot)
          }
          .frame(width: 300)
        }
      }
    • 8:38 - Customizing Vertical Alignment

      // Customizing vertical alignment
      
      HStack(alignment: .bottom) {
        Image("RobotHead")
          .border(.red)
        Color.blue
          .frame(width: 100, height: 100)
          .border(.red)
      }
      .border(.yellow)
    • 8:52 - Customizing Depth Alignment

      // Customizing depth alignments
      
      struct RobotProfile: View {
        let robot: Robot
      
        var body: some View {
          VStackLayout().depthAlignment(.front) {
            ResizableRobotView(asset: robot.model3DAsset)
            RobotNameCard(robot: robot)
          }
          .frame(width: 300)
        }
      }
    • 9:45 - Robot Favorite Row

      struct FavoriteRobotsRow: View {
        let robots: [Robot]
      
        var body: some View {
          HStack {
            RobotProfile(robot: robots[2])
            RobotProfile(robot: robots[0])
            RobotProfile(robot: robots[1])
          }
        }
      }
    • 10:27 - Custom Depth Alignment ID

      // Defining a custom depth alignment guide
      
      struct DepthPodiumAlignment: DepthAlignmentID {
        static func defaultValue(in context: ViewDimensions3D) -> CGFloat {
          context[.front]
        }
      }
      
      extension DepthAlignment {
        static let depthPodium = DepthAlignment(DepthPodiumAlignment.self)
      }
    • 10:51 - Customizing Depth Alignment Guides

      // Views can customize their alignment guides
      
      struct FavoritesRow: View {
        let robots: [Robot]
      
        var body: some View {
          HStackLayout().depthAlignment(.depthPodium) {
              RobotProfile(robot: robots[2])
              RobotProfile(robot: robots[0])
                .alignmentGuide(.depthPodium) {
                  $0[DepthAlignment.back]
                }
              RobotProfile(robot: robots[1])
            		.alignmentGuide(.depthPodium) {
                  $0[DepthAlignment.center]
                }
          }
        }
      }
    • 12:00 - Rotation3DEffect

      // Rotate views using visual effects
      
      Model3D(named: "ToyRocket")
        .rotation3DEffect(.degrees(45), axis: .z)
    • 12:10 - Rotation3DLayout

      // Rotate using any axis or angle
      
      HStackLayout().depthAlignment(.front) {
        RocketDetailsCard()
        Model3D(named: "ToyRocket")
        	.rotation3DLayout(.degrees(isRotated ? 45 : 0), axis: .z)
      }
    • 14:42 - Pet Radial Layout

      // Custom radial Layout
      
      struct PetRadialLayout: View {
        let pets: [Pet]
      
        var body: some View {
          MyRadialLayout {
            ForEach(pets) { pet in
              PetImage(pet: pet)
            }
          }
        }
      }
    • 14:56 - Rotated Robot Carousel

      struct RobotCarousel: View {
        let robots: [Robot]
      
        var body: some View {
      		VStack {
            Spacer()
            MyRadialLayout {
              ForEach(robots) { robot in
                ResizableRobotView(asset: robot.model3DAsset)
                	.rotation3DLayout(.degrees(-90), axis: .x)
              }
            }
            .rotation3DLayout(.degrees(90), axis: .x)
        }
      }
    • 17:00 - Spatial Container

      // Aligning views in 3D space
      
      SpatialContainer(alignment: .topTrailingBack) {
        LargeBox()
        MediumBox()
        SmallBox()
      }
    • 17:35 - Spatial Overlay

      // Aligning overlayed content
      
      LargeBox()
        .spatialOverlay(alignment: .bottomLeadingFront) {
          SmallBox()
        }
    • 17:47 - Selection Ring Spatial Overlay

      struct RobotCarouselItem: View {
        let robot: Robot
        let isSelected: Bool
      
        var body: some View {
          ResizableRobotView(asset: robot.model3DAsset)
      			.spatialOverlay(alignment; .bottom) {
              if isSelected {
                ResizableSelectionRingModel()
              }
        }
      }
    • 18:32 - DebugBorder3D

      extension View {
        func debugBorder3D(_ color: Color) -> some View {
          spatialOverlay {
      			ZStack {
      				Color.clear.border(color, width: 4)
              ZStack {
                Color.clear.border(color, width: 4)
                Spacer()
                Color.clear.border(color, width: 4)
              }
              .rotation3DLayout(.degrees(90), axis: .y)
      				Color.clear.border(color, width: 4)
            }
          }
        }

Developer Footer

  • ビデオ
  • WWDC25
  • SwiftUIの空間レイアウトの紹介
  • メニューを開く メニューを閉じる
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    メニューを開く メニューを閉じる
    • アクセシビリティ
    • アクセサリ
    • App Extension
    • App Store
    • オーディオとビデオ(英語)
    • 拡張現実
    • デザイン
    • 配信
    • 教育
    • フォント(英語)
    • ゲーム
    • ヘルスケアとフィットネス
    • アプリ内課金
    • ローカリゼーション
    • マップと位置情報
    • 機械学習
    • オープンソース(英語)
    • セキュリティ
    • SafariとWeb(英語)
    メニューを開く メニューを閉じる
    • 英語ドキュメント(完全版)
    • 日本語ドキュメント(一部トピック)
    • チュートリアル
    • ダウンロード(英語)
    • フォーラム(英語)
    • ビデオ
    Open Menu Close Menu
    • サポートドキュメント
    • お問い合わせ
    • バグ報告
    • システム状況(英語)
    メニューを開く メニューを閉じる
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles(英語)
    • フィードバックアシスタント
    メニューを開く メニューを閉じる
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(英語)
    • News Partner Program(英語)
    • Video Partner Program(英語)
    • セキュリティ報奨金プログラム(英語)
    • Security Research Device Program(英語)
    Open Menu Close Menu
    • Appleに相談
    • Apple Developer Center
    • App Store Awards(英語)
    • Apple Design Awards
    • Apple Developer Academy(英語)
    • WWDC
    Apple Developerアプリを入手する
    Copyright © 2025 Apple Inc. All rights reserved.
    利用規約 プライバシーポリシー 契約とガイドライン