Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2025 DesignSpatial ComputingSwiftUI & UI Frameworks

WWDC25 · 20 min · Design / Spatial Computing / SwiftUI & UI Frameworks

Meet SwiftUI spatial layout

Explore new tools for building spatial experiences using SwiftUI. Learn the basics of 3D SwiftUI views on visionOS, customize existing layouts with depth alignments, and use modifiers to rotate and position views in space. Discover how to use spatial containers to align views in the same 3D space, helping you create immersive and engaging apps.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 26 snippets

Robot Image Frame swift · at 3:02 ↗
// Some views have fixed frames

Image("RobotHead")
  .border(.red)
Color Frame swift · at 3:05 ↗
// Some views have flexible frames

Color.blue
  .border(.red)
Layout Composed Frame swift · at 3:15 ↗
// Layouts compose the frames of their children

VStack {
  Image("RobotHead")
    .border(.red)
  Image("RobotHead")
    .border(.red)
}
.border(.yellow)
Model3D Frame swift · at 4:00 ↗
// Some views have fixed depth

Model3D(named: "Robot")
  .debugBorder3D(.red)
Zero Depth Views swift · at 4:25 ↗
// 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)
}
RealityView Depth swift · at 4:41 ↗
// RealityView takes up all available space including depth

RealityView { content in
  // Setup RealityView content
}
.debugBorder3D(.red)
GeometryReader3D Depth swift · at 4:56 ↗
// GeometryReader3D uses all available depth

GeometryReader3D { proxy in
  // GeometryReader3D content
}
.debugBorder3D(.red)
Model3D scaledToFit3D swift · at 5:01 ↗
// Scaling a Model3D to fit available space

Model3D(url: robotURL) {aresolved in
  resolved.resizable()
}aplaceholder: {
  ProgressView()
}
.scaledToFit3D()
.debugBorder3D(.red)
ZStack depth swift · at 6:15 ↗
// ZStack composes subview depths

ZStack {
  Model3D(named: "LargeRobot")
    .debugBorder3D(.red)
  Model3D(named: "BabyBot")
    .debugBorder3D(.red)
}
.debugBorder3D(.yellow)
ZStack with RealityView swift · at 6:33 ↗
// ZStack composes subview depths

ZStack {
  RealityView { ... }
    .debugBorder3D(.red)
  Model3D(named: "BabyBot")
    .debugBorder3D(.red)
}
.debugBorder3D(.yellow)
Layouts are 3D swift · at 6:57 ↗
// HStack also composes subview depths

HStack {
  Model3D(named: "LargeRobot")
    .debugBorder3D(.red)
  Model3D(named: "BabyBot")
    .debugBorder3D(.red)
}
.debugBorder3D(.yellow)
ResizableRobotView swift · at 7:50 ↗
struct ResizableRobotView: View {
  let asset: Model3DAsset

  var body: some View {
    Model3D(asset: asset) { resolved in
      resolved
        .resizable()
    }
    .scaledToFit3D()
  }
}
Robot Profile 1 swift · at 8:11 ↗
//`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)
  }
}
Customizing Vertical Alignment swift · at 8:38 ↗
// Customizing vertical alignment

HStack(alignment: .bottom) {
  Image("RobotHead")
    .border(.red)
  Color.blue
    .frame(width: 100, height: 100)
    .border(.red)
}
.border(.yellow)
Customizing Depth Alignment swift · at 8:52 ↗
// 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)
  }
}
Robot Favorite Row swift · at 9:45 ↗
struct FavoriteRobotsRow: View {
  let robots: [Robot]

  var body: some View {
    HStack {
      RobotProfile(robot: robots[2])
      RobotProfile(robot: robots[0])
      RobotProfile(robot: robots[1])
    }
  }
}
Custom Depth Alignment ID swift · at 10:27 ↗
// 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)
}
Customizing Depth Alignment Guides swift · at 10:51 ↗
// 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]
          }
    }
  }
}
Rotation3DEffect swift · at 12:00 ↗
// Rotate views using visual effects

Model3D(named: "ToyRocket")
  .rotation3DEffect(.degrees(45), axis: .z)
Rotation3DLayout swift · at 12:10 ↗
// Rotate using any axis or angle

HStackLayout().depthAlignment(.front) {
  RocketDetailsCard()
  Model3D(named: "ToyRocket")
  	.rotation3DLayout(.degrees(isRotated ? 45 : 0), axis: .z)
}
Pet Radial Layout swift · at 14:42 ↗
// Custom radial Layout

struct PetRadialLayout: View {
  let pets: [Pet]

  var body: some View {
    MyRadialLayout {
      ForEach(pets) { pet in
        PetImage(pet: pet)
      }
    }
  }
}
Rotated Robot Carousel swift · at 14:56 ↗
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)
  }
}
Spatial Container swift · at 17:00 ↗
// Aligning views in 3D space

SpatialContainer(alignment: .topTrailingBack) {
  LargeBox()
  MediumBox()
  SmallBox()
}
Spatial Overlay swift · at 17:35 ↗
// Aligning overlayed content

LargeBox()
  .spatialOverlay(alignment: .bottomLeadingFront) {
    SmallBox()
  }
Selection Ring Spatial Overlay swift · at 17:47 ↗
struct RobotCarouselItem: View {
  let robot: Robot
  let isSelected: Bool

  var body: some View {
    ResizableRobotView(asset: robot.model3DAsset)
			.spatialOverlay(alignment; .bottom) {
        if isSelected {
          ResizableSelectionRingModel()
        }
  }
}
DebugBorder3D swift · at 18:32 ↗
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)
      }
    }
  }

Resources