-
Conheça o layout espacial do SwiftUI
Explore novas ferramentas para criar experiências espaciais usando o SwiftUI. Aprenda os fundamentos das visualizações 3D do SwiftUI no visionOS, personalize os layouts existentes com alinhamentos de profundidade e use modificadores para girar e posicionar as visualizações no espaço. Saiba como usar contêineres espaciais para alinhar visualizações no mesmo espaço 3D e criar apps imersivos e interessantes.
Capítulos
- 0:00 - Introdução
- 2:47 - Visualizações 3D
- 7:18 - Alinhamentos de profundidade
- 11:41 - Layout de rotação
- 16:28 - SpatialContainer
- 19:22 - Próximas etapas
Recursos
- Canyon Crosser: Building a volumetric hike-planning app
- Human Interface Guidelines: Designing for visionOS
Vídeos relacionados
WWDC25
WWDC22
WWDC19
-
Buscar neste vídeo...
-
-
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) } } }
-
-
- 0:00 - Introdução
Os novos recursos de layout 3D do SwiftUI no visionOS 26 permitem criar apps 3D usando a sintaxe declarativa do SwiftUI. Esses recursos se baseiam em conceitos de layout 2D existentes e têm compatibilidade integrada com animações, redimensionamento e gerenciamento de estado. O exemplo do BOT-anist demonstra como as pessoas podem personalizar e catalogar robôs em um jardim virtual. Se você não conhece os layouts do SwiftUI, confira “Building custom views with SwiftUI” e “Compose custom layouts with SwiftUI” antes de avançar neste conteúdo.
- 2:47 - Visualizações 3D
No SwiftUI, o sistema de layout calcula as posições de largura, altura, X e Y para cada visualização em um app. Alguns modos de visualização têm quadros fixos, e outros têm quadros flexíveis que ocupam o espaço disponível fornecido pelo item principal. No visionOS, esse conceito se estende a três dimensões. Cada visualização agora tem profundidade e posição Z, além de largura e altura. O layout se comporta de forma semelhante ao 2D, mas se aplica ao espaço 3D. As visualizações podem ter profundidade fixa, flexível ou zero. Usar o GeometryReader3D e o Model3D com o modificador resizable podem ocupar toda a profundidade. O novo modificador scaledToFit3D ajuda a manter as proporções durante o redimensionamento. As janelas têm uma proposta de profundidade raiz fixa, e os volumes têm profundidade redimensionável. O conteúdo fora dessa profundidade pode ser cortado pelo sistema. O ZStack compõe profundidades e o VStack compõe alturas, e os tipos de layout existentes, como HStack e VStack, já são 3D no visionOS, aplicando comportamentos padrão de profundidade, como alinhar os elementos pela parte de trás.
- 7:18 - Alinhamentos de profundidade
Os alinhamentos de profundidade são um novo recurso no visionOS 26 e permitem posicionar visualizações personalizadas no espaço 3D, semelhante a como os alinhamentos vertical e horizontal funcionam em 2D. Essa abordagem é útil ao criar janelas volumétricas ou exibir modelos 3D. O exemplo mostra como usar o alinhamento de profundidade para melhorar a legibilidade de uma visualização de perfil de robô. Ao aplicar o modificador de alinhamento .front ao cartão de nome do robô, ele é movido para frente, facilitando a visualização. Para cenários mais complexos, você pode criar alinhamentos personalizados. O exemplo mostra como definir um DepthPodiumAlignment personalizado para distribuir três visualizações de perfis de robôs, com o favorito mais próximo do espectador, criando destaque.
- 11:41 - Layout de rotação
O visionOS 26 introduz o novo modificador rotation3DLayout para resolver as limitações do modificador rotation3DEffect existente. O modificador rotation3DEffect aplica apenas rotações visuais sem afetar o sistema de layout, causando problemas ao girar visualizações dentro de contêineres como HStacks. O rotation3DEffect ainda é útil quando você deseja animar uma visualização sem afetar os quadros de outras. O rotation3DLayout modifica o quadro de uma visualização girada no layout do sistema, possibilitando ajustes adequados de dimensionamento e posicionamento, o que permite criar layouts 3D mais complexos. Por exemplo, você pode usá-lo para criar um carrossel de robôs na horizontal girando um RadialLayout personalizado 90 graus no eixo X. Cada robô no carrossel é girado no sentido contrário para ficar em pé. Ajustes adicionais, como o uso do VStack com um espaçador, podem ser aplicados para posicionar o carrossel nivelado com a base do volume, criando uma interface 3D polida e bonita.
- 16:28 - SpatialContainer
O SpatialContainer e o SpatialOverlay são novas ferramentas do SwiftUI para layout 3D. O SpatialContainer permite aninhar várias visualizações no espaço 3D com opções de alinhamento, e o SpatialOverlay sobrepõe uma visualização a outra. O exemplo usa o SpatialOverlay para criar um anel de seleção para robôs em um carrossel, alinhando-o à parte inferior do robô. O modificador debugBorder3D de exemplo é demonstrado como uma extensão da visualização, usando SpatialOverlay, ZStacks e rotation3DLayout para adicionar bordas 3D a qualquer Model3D para depuração.
- 19:22 - Próximas etapas
O SwiftUI agora permite o desenvolvimento de apps 3D usando modificadores 2D conhecidos e novas APIs 3D. Você pode combinar o SwiftUI com o RealityKit para melhorar a funcionalidade. Confira exemplos na sessão “Melhor juntos: SwiftUI e RealityKit”.