-
Découvrez la disposition spatiale SwiftUI
Explorez de nouveaux outils pour créer des expériences spatiales avec SwiftUI. Apprenez les bases des vues SwiftUI en 3D sur visionOS, personnalisez des dispositions existantes à l'aide d'alignements en profondeur, et utilisez des modificateurs pour faire pivoter et positionner les vues dans l'espace. Découvrez comment utiliser les conteneurs spatiaux pour aligner des vues dans un même espace 3D et créer ainsi des apps immersives et captivantes.
Chapitres
- 0:00 - Introduction
- 2:47 - Vues 3D
- 7:18 - Alignements de profondeur
- 11:41 - Disposition de rotation
- 16:28 - Conteneurs spatiaux
- 19:22 - Étapes suivantes
Ressources
- Canyon Crosser: Building a volumetric hike-planning app
- Human Interface Guidelines: Designing for visionOS
Vidéos connexes
WWDC25
WWDC22
WWDC19
-
Rechercher dans cette vidéo…
-
-
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 - Introduction
SwiftUI prend désormais en charge les mises en forme 3D sur visionOS 26, vous permettant de créer des apps 3D avec une syntaxe déclarative. Ces capacités s'appuient sur les principes de mise en forme 2D existants et intègrent nativement les animations, les redimensionnements et la gestion d'état. L'app exemple BOTanist montre comment les utilisateurs peuvent personnaliser et cataloguer des robots dans un jardin virtuel. Si vous débutez avec SwiftUI, consultez « Building custom layouts with SwiftUI » et « Compose custom layouts with SwiftUI » avant d'aller plus loin.
- 2:47 - Vues 3D
Dans SwiftUI, le système de mise en forme calcule la largeur, hauteur, et les positions X/Y pour chaque vue dans l'app. Certaines vues ont des cadres fixes, d'autres s'ajustent à l'espace disponible fourni par leur parent. Sur visionOS, cette logique s'étend à trois dimensions. Chaque vue dispose désormais d'une profondeur et d'une position Z en plus de la largeur et de la hauteur. Le système de mise en forme fonctionne comme en 2D, mais dans l'espace 3D. Les vues peuvent avoir une profondeur fixe, flexible ou nulle. GeometryReader3D et Model3D avec le modificateur « redimensionnable » permettent d'occuper toute la profondeur disponible. Le modificateur scaledToFit3D aide à conserver les proportions lors du redimensionnement. Les fenêtres ont une proposition de profondeur racine fixe, tandis que les volumes sont redimensionnables. Le contenu en dehors de cette profondeur peut être tronqué par le système. ZStack compose la profondeur comme VStack compose la hauteur. Les mises en forme existantes comme HStack et VStack sont automatiquement 3D sur visionOS, avec des comportements par défaut pour la profondeur (alignement arrière).
- 7:18 - Alignements de profondeur
Les alignements en profondeur sont une nouvelle fonctionnalité de visionOS 26, permettant de positionner les vues en 3D comme les alignements verticaux et horizontaux en 2D. Cette méthode est utile pour créer des fenêtres volumétriques ou afficher des modèles 3D. L'exemple montre comment utiliser l'alignement en profondeur pour améliorer la lisibilité d'une fiche profil de robot. En appliquant le modificateur « .front » à la carte du nom du robot, elle est déplacée au premier plan pour une meilleure visibilité. Pour les cas complexes, vous pouvez créer vos propres alignements en profondeur personnalisés. L'exemple montre comment définir un alignement personnalisé DepthPodiumAlignment pour répartir trois fiches profil de robots en profondeur, avec le robot favori placé au premier plan pour souligner son importance.
- 11:41 - Disposition de rotation
visionOS 26 introduit le nouveau modificateur rotation3DLayout, qui corrige les limites du modificateur existant rotation3DEffect. Le modificateur rotation3DEffect applique uniquement une rotation visuelle sans affecter le système de mise en forme, ce qui pose problème dans les conteneurs comme HStack. rotation3DEffect reste utile lorsque vous voulez animer une vue sans influencer le cadre des autres vues. Le modificateur rotation3DLayout modifie le cadre d'une vue dans le système de mise en forme, permettant un ajustement précis de taille et de position, pour des compositions 3D plus complexes. Par exemple, vous pouvez créer un carrousel horizontal de robots en appliquant une rotation de 90° sur l'axe X à un RadialLayout personnalisé. Chaque robot dans le carrousel est ensuite contre-roté pour rester droit. Des ajustements supplémentaires, comme l'utilisation d'un VStack avec un Spacer, peuvent positionner le carrousel au ras du bas du volume, pour une interface 3D soignée et esthétique.
- 16:28 - Conteneurs spatiaux
« SpatialContainer » et « SpatialOverlay » sont de nouveaux outils SwiftUI pour la mise en forme 3D. SpatialContainer permet d'imbriquer plusieurs vues dans l'espace 3D avec des options d'alignement, tandis que SpatialOverlay superpose une vue à une autre. L'exemple utilise SpatialOverlay pour créer un anneau de sélection pour les robots dans le carrousel, aligné au bas de chaque robot. Le modificateur debugBorder3D, une extension de View, combine SpatialOverlay, ZStack et rotation3DLayout pour ajouter une bordure 3D à n'importe quel Model3D à des fins de débogage.
- 19:22 - Étapes suivantes
SwiftUI permet désormais de développer des apps 3D en s'appuyant sur des modificateurs 2D familiers et sur de nouvelles API 3D. Vous pouvez combiner SwiftUI avec RealityKit pour plus de possibilités ; consultez les exemples dans « Better Together: SwiftUI and RealityKit ».