View in English

  • Global Nav Open Menu Global Nav Close Menu
  • Apple Developer
Search
Cancel
  • Apple Developer
  • News
  • Discover
  • Design
  • Develop
  • Distribute
  • Support
  • Account
Only search within “”

Quick Links

5 Quick Links

Vidéos

Ouvrir le menu Fermer le menu
  • Collections
  • Sujets
  • Toutes les vidéos
  • À propos

Plus de vidéos

  • À propos
  • Résumé
  • Transcription
  • Code
  • 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éo HD
      • Vidéo SD

    Vidéos connexes

    WWDC25

    • Mieux ensemble : SwiftUI et RealityKit

    WWDC22

    • Compose custom layouts with SwiftUI

    WWDC19

    • Building Custom Views with SwiftUI
  • Rechercher dans cette vidéo…

    Bonjour, bienvenue dans « Découvrir la disposition spatiale SwiftUI ». Je suis Trevor, ingénieur dans l’équipe SwiftUI. Aujourd’hui, nous explorons des techniques permettant de créer de délicieuses expériences spatiales grâce à SwiftUI.

    J’ai profité des nouvelles capacités de disposition spatiale de SwiftUI, en développant une app que j’adore : BOT-anist.

    L’app permet de personnaliser des robots amusants à partir de divers blocs de construction, couleurs et matériaux. Vous pouvez ensuite utiliser vos nouveaux robots pour entretenir votre propre jardin virtuel. J’adore construire ces petits robots, et récemment j’ai travaillé sur de nouvelles vues pour cataloguer mes créations.

    Maintenant, vous pouvez personnaliser un robot, l’enregistrer et l’ajouter à votre collection.

    Je suis ravi de vous montrer de nouvelles scènes 3D pour accéder aux robots. J’ai créé toutes ces expériences avec SwiftUI.

    Si vous avez déjà créé des expériences 3D sur visionOS, vous avez peut-être déjà utilisé RealityKit.

    RealityKit est un excellent framework pour créer des apps 3D, en particulier celles qui ont des comportements complexes comme les simulations physiques.

    Si vous avez l’habitude de SwiftUI, vous pouvez intégrer la syntaxe déclarative que vous connaissez déjà. Et vous passer de la puissance de RealityKit dans votre app. Dans visionOS 26, vous pouvez désormais utiliser les outils et les idées de disposition 2D existants de SwiftUI pour créer des applications 3D.

    La disposition SwiftUI vous donne une prise en charge intégrée des animations, du redimensionnement et de la gestion des états : si je retire un bot du carrousel, SwiftUI peut animer les positions et les tailles de tous les autres robots pour s’adapter à plus ou moins d’espace.

    Et le redimensionnement du volume redimensionne automatiquement le carrousel et chaque robot qu’il contient. Examinons les nouveaux outils que j’ai utilisés pour créer ces arrangements d’automates.

    Ces extensions 3D du système de disposition de SwiftUI s’appuient sur des concepts de disposition 2D existants.

    Si vous débutez avec les dispositions SwiftUI, consultez les vidéos « Build custom views with SwiftUI » et « Compose custom layouts with SwiftUI » avant d’aborder ce contenu. Dans cette vidéo, nous examinons les bases des vues 3D SwiftUI sur visionOS, comment personnaliser les dispositions existantes avec des alignements de profondeur. rotation3DLayout, un nouveau modificateur pour la rotation des vues dans le système de disposition, et enfin, SpatialContainer et spatialOverlay comme moyen d’aligner les vues dans le même espace 3D.

    Parlons des vues et du système de disposition.

    Pour chaque vue de votre app, SwiftUI calcule une largeur, une hauteur, une position X et Y.

    Certaines vues, comme une image non redimensionnable, ont un cadre fixe, qui correspond à la taille de la ressource.

    D’autres vues, comme cette Color, ont des cadres flexibles et occupent tout l’espace qui leur est fourni par un parent.

    Les dispositions composent leurs enfants dans un cadre final. Le cadre de ce VStack, représenté en jaune, est déterminé par l’espace disponible et les enfants qu’il contient. Ici, sa hauteur est la somme des deux vues d’images à l’intérieur. visionOS se comporte de la même manière, mais les vues y sont en 3D. Le système de disposition a les mêmes comportements, désormais appliqués à trois dimensions au lieu de deux.

    Cela signifie que pour chacune de vos vues, en plus de la largeur et de la hauteur, SwiftUI calcule également une profondeur et une position Z. J’utilise souvent le modificateur de bordure pour visualiser des images 2D sur iOS.

    Ici, j’ai créé mon propre modificateur debugBorder3D pour visualiser les images 3D sur visionOS. Je vous montre comment j’ai construit ce modificateur à la fin de cette vidéo à l’aide de quelques-unes des API que vous découvrirez en attendant.

    Le debugBorder3D montre que Model3D se comporte de la même manière qu’une image, mais en trois dimensions au lieu de deux, occupant une largeur, une hauteur et une profondeur fixes. Toutes les vues soient en 3D, mais certaines n’ont aucune profondeur.

    La plupart des vues que vous utilisez pour créer des expériences planes, comme Image, Color et Text, n’occupent aucune profondeur, ce qui signifie qu’elles se comportent exactement comme sur iOS.

    Certaines vues ont une profondeur flexible, de la même manière que Color occupe tout l’espace disponible par défaut. Sur visionOS, certaines vues, comme RealityView, occupent par défaut toute la profondeur disponible.

    GeometryReader3D a ce même comportement de dimensionnement flexible, ainsi que Model3D avec le modificateur redimensionnable appliqué, qui a étiré notre ami robot comme du caramel pour s’adapter à toute la largeur de cette fenêtre. Il a un visage un peu allongé dans ce format d’image, quand même. J’aimerais lui redonner ses proportions d’origine tout en l’adaptant à l’espace disponible.

    Avec le nouveau modificateur scaledToFit3D et resizable(), mon robot peut conserver le rapport hauteur/largeur du modèle tout en changeant de taille pour s’adapter à la largeur, à la hauteur et maintenant à la profondeur disponibles.

    D’où vient cette profondeur disponible ? Tout comme la largeur et la hauteur, le contenu Fenêtres reçoit une proposition de profondeur racine. Contrairement à la largeur et à la hauteur, qui peuvent être redimensionnables, cette proposition de profondeur est fixe pour les fenêtres. En dehors de cette profondeur, votre contenu peut être coupé par le système.

    De même, un volume propose une largeur, une hauteur et une profondeur à son contenu, mais dans un volume, la profondeur est également redimensionnable. Consultez « Designing for visionOS » dans les Directives d’interface humaine pour plus de détails sur le moment d’utiliser un volume ou une fenêtre.

    Certaines vues peuvent modifier ces propositions de profondeur pour des vues contenues. De même qu’un VStack compose les hauteurs de ses sous-vues, ZStack compose les profondeurs. La profondeur de ce ZStack est donc la profondeur nécessaire pour faire tenir les deux robots empilés l’un devant l’autre.

    Et de même que VStack peut proposer différentes hauteurs à ses sous-vues en fonction de facteurs tels que l’espace disponible, le nombre d’enfants et le type d’enfants, ZStack peut proposer différentes profondeurs à ses enfants en fonction des mêmes facteurs. Ici, le RealityView pousse le robot vers l’avant dans le ZStack, remplissant toute la profondeur disponible dans la scène.

    Les types de disposition et les piles existants sont en fait en 3D sur visionOS et appliquent des comportements par défaut adéquats pour la profondeur. Dans cet exemple, le HStack transmet une proposition de profondeur de son parent et établit sa propre profondeur pour s’adapter parfaitement aux deux modèles à l’intérieur.

    Le HStack aligne également le dos de ces deux robots par défaut.

    Nous appelons ce concept l’alignement de profondeur. Les alignements de profondeur sont un nouvel outil permettant de personnaliser les types de disposition SwiftUI existants afin de mieux prendre en charge les vues 3D et la profondeur. Si vous avez travaillé avec des alignements verticaux ou horizontaux, ceux-ci vous sembleront familiers. J’aimerais créer une nouvelle fenêtre volumétrique pour afficher mes robots préférés avec le nom et la description de chacun. Tout d’abord, mettons à jour le code de notre robot Model3D pour le rendre plus réutilisable.

    Je commence avec un Model3D qui est redimensionné pour s’adapter.

    Je le refactorise pour utiliser le nouveau type Model3DAsset, qui me permet de précharger le modèle pour mon robot. J’englobe tout cela dans un nouveau ResizableRobotView, que je peux utiliser dans toute l’app. Je supprime également le debugBorder3D pour le moment.

    Maintenant, je crée un RobotProfile en utilisant un VStack contenant un ResizableRobotView, plus une RobotNameCard avec quelques infos sur le bot.

    Mais il y a un problème.

    Cette carte est difficile à lire car elle est placée à l’arrière du VStack, et elle est un peu perdue derrière le modèle du robot.

    Comme vous pouvez configurer dans HStack pour aligner son contenu sur le bord central, supérieur ou inférieur, vous pouvez configurer la façon dont les vues sont alignées en profondeur sur visionOS.

    Par défaut, les types Piles et Disposition utilisent un alignement de profondeur à l’arrière. Dans visionOS 26, vous pouvez désormais personnaliser les alignements de profondeur sur n’importe quel type de disposition.

    Je vais mettre à jour le RobotProfile pour utiliser VStackLayout.

    Je peux donc appliquer le modificateur depthAlignement. Je demande l’alignement .front ici.

    Je peux également utiliser le guide central ou arrière.

    Mais je pense que l’avant est le bon choix pour rendre cette carte de visite de robot lisible.

    Désormais, je ne peux plus oublier Zapper Ironheart et sa connaissance encyclopédique de faits obscurs.

    Les alignements de profondeur standard avant, arrière ou central sont parfaits si ceux-ci vous conviennent. Mais si vous avez besoin de quelque chose de plus complexe ?

    J’ai créé un volume pour montrer mes trois robots préférés avec trois de ces vues de profil de robot dans un HStack. Greg-gear Mendel est mon robot préféré, et j’aimerais le mettre en avant par rapport aux deux autres.

    En fait, j’ai pensé à une sorte de podium de profondeur où plus j’aime l’un de ces robots, plus il est proche de moi. Robot 1 est donc le plus proche, puis 2, puis 3.

    Vu de dessus, je veux que cela ressemble à ça, où l’arrière du premier robot est aligné en profondeur sur le centre du 2e robot et l’avant du 3e. Pour cela, j’ai besoin d’un alignement de profondeur personnalisé.

    Tout d’abord, je définis une nouvelle structure conforme au protocole DepthAlignmentID.

    J’applique la seule exigence, qui est la valeur par défaut pour cet alignement.

    J’utilise le guide d’alignement avant par défaut pour notre DepthPodiumAlignment.

    Ensuite, je définis une constante statique sur l’alignement de profondeur qui utilise ce nouveau type DepthAlignmentID.

    Maintenant, je peux utiliser ce guide d’alignement depthPodium comme alignement de profondeur sur le HStack contenant chaque robot.

    Cela aligne tous les robots sur leur face avant compte tenu de la valeur par défaut que nous venons de spécifier pour ce guide.

    Maintenant, je personnalise le guide d’alignement depthPodium sur le robot suivant pour l’aligner sur son centre de profondeur.

    Je modifie le robot central pour aligner son dos sur le guide depthPodium.

    Le robot de tête continue d’utiliser son guide avant par défaut pour cet alignement.

    Le voici dans le simulateur.

    Avec cet agencement en profondeur, Greg-gear Mendel apparaît clairement comme mon préféré.

    Les alignements de profondeur sont parfaits quand vous voulez modifier la position de la profondeur dans une disposition existante. Mais, si vous voulez construire quelque chose d’encore plus axé sur la profondeur ? Rotation Layout est un excellent outil pour les cas d’utilisation 3D plus avancés. Vous connaissez peut-être le modificateur rotation3DEffect existant, qui applique un effet visuel à une vue pour la faire pivoter autour d’un axe donné.

    Ce modificateur est idéal pour les rotations de base.

    Mais si nous plaçons notre modèle dans un HStack avec une fiche descriptive et que nous faisons pivoter la fusée de 90 degrés le long de l’axe Z, elle chevauche la fiche et manque de place.

    Si nous appliquons des images filaires de débogage avant et après l’effet de rotation, il est un peu plus facile de comprendre ce qui se passe. L’image filaire rouge fixe est tournée par l’effet, mais la bleue en pointillés me montre où le système de disposition situe la géométrie de la fusée. Le HStack se dimensionne et place son contenu par rapport à ce cadre bleu. Ceux-ci ne s’alignent pas. Parce que les effets visuels n’ont pas d’impact sur la disposition. Donc, le HStack ignore la géométrie pivotée de la fusée quand j’applique rotation3DEffect.

    Cela est vrai pour tous les effets visuels, y compris scaleEffect et les décalages.

    Dans tous ces cas, le système de disposition n’ajuste pas la taille ou le placement des vues après modification. C’est très utile lorsque vous souhaitez animer une vue sans impacter les images des autres qui l’entourent.

    Mais que faire dans le cas contraire ? Comment repositionner cette fusée pivotée ?

    Bonne nouvelle. visionOS 26 comprend un nouveau modificateur rotation3DLayout, qui change le cadre d’une vue pivotée dans le système de disposition. Lorsque je l’applique à mon modèle de fusée, le HStack peut ajuster sa taille et son placement pour donner à la fusée et à la fiche descriptive beaucoup d’espace.

    rotation3DLayout prend en charge les rotations sur n’importe quel angle et axe, et je peux donc faire pivoter ma fusée à 45 degrés, ce qui donne vraiment l’impression qu’elle fonce vers l’espace.

    J’applique une image filaire de débogage avant et après le modificateur rotation3DLayout. Cela montre le cadre pivoté de la fusée en rouge. L’image filaire bleue montre le cadre de la vue modifiée dans le système de disposition. Notez que le cadre de sélection bleu est aligné sur l’axe du parent et s’adapte parfaitement au cadre pivoté en rouge.

    Voyons maintenant comment utiliser rotation3DLayout pour construire le carrousel de robots que je vous ai montré au début de cette vidéo.

    Je commencer par emprunter le RadialLayout de « Compose custom layouts with SwiftUI ».

    Ce type de disposition personnalisée place les vues dans un cercle avec la circonférence définie par la largeur et la hauteur disponibles.

    MyRadialLayout a été écrit à l’origine pour placer des vues 2D sur iOS, mais il fonctionne très bien sur visionOS.

    Même lorsqu’il positionne des modèles 3D de robots au lieu d’images 2D d’animaux de compagnie, nous pouvons utiliser un ForEach pour placer nos Model3D redimensionnables de chaque robot dans cette disposition personnalisée.

    C’est bien, mais cela reste une expérience verticale. Je veux que mes robots soient orientés horizontalement dans le volume.

    J’applique une rotation3DLayout à la disposition radiale en faisant pivoter la vue de 90 degrés le long de l’axe X. Ce qui était auparavant la hauteur du carrousel définit désormais la profondeur de la vue pivotée dans le système de disposition. Mon carrousel est orienté correctement maintenant, mais mes robots sont couchés, les fainéants.

    Nous pouvons les relever en retournant chaque robot à l’intérieur du ForEach par une deuxième rotation3DEffect de -90 degrés le long de l’axe X. Ces droïdes somnolents sont maintenant au garde-à-vous. Il ne reste plus qu’une dernière chose à corriger. Le carrousel est aligné au centre du volume. J’aimerais que le carrousel affleure la plaque de base du volume.

    C’est plus facile à remarquer avec un debugBorder3D appliqué à l’ensemble du carrousel.

    Je peux utiliser la même stratégie que pour une disposition 2D. Je veux pousser le carrousel vers le bas à l’intérieur d’un VStack avec un Spacer par dessus. Mes robots ont fière allure au bas du volume maintenant. Examinons deux autres outils dans votre trousse de disposition 3D, SpatialContainer et spatialOverlay. Il y a une autre fonctionnalité que j’aimerais ajouter à notre carrousel de robots. En touchant un robot devrait le sélectionner, afficher un menu de commandes ainsi qu’un anneau en bas du modèle, indiquant qu’il est sélectionné. Cet anneau est également représenté sous la forme d’un Model3D. Nous voulons que l’anneau remplisse le même espace 3D que notre robot. Nous ne voulons pas qu’il s’empile le long d’un axe. Nous avons besoin d’un nouvel outil qui place les modèles dans le même espace 3D.

    La nouvelle API SpatialContainer vous permet de placer plusieurs vues dans le même espace 3D comme une série de poupées gigognes.

    Vous pouvez appliquer un alignement tridimensionnel à toutes les vues. Ici, nous alignons tous les enfants en fonction de leur guide d’alignement bottomFront.

    Et ici, suivant leur guide topTrailingBack.

    spatialOverlay est un outil similaire, qui permet de superposer une vue unique dans le même espace 3D qu’une autre.

    Comme SpatialContainer, il prend en charge les alignements 3D.

    Je n’ai que deux vues à aligner, le robot et l’anneau de sélection. Et je ne m’intéresse qu’à la géométrie du robot. Cela me convient que l’anneau soit redimensionné pour correspondre à la taille de mon robot. Utilisons donc un spatialOverlay pour appliquer les visuels sélectionnés du robot.

    J’ajoute un modificateur spatialOverlay à notre modèle de robot. Et si elle est marquée comme sélectionnée, placez la vue de l’anneau redimensionnable comme contenu. Nous utilisons un alignement inférieur pour aligner le bas de l’anneau avec le bas de notre robot.

    Je trouve que notre carrousel de robots a fière allure. Et il est facile de l’améliorer encore avec toutes les API SwiftUI composables existantes.

    Résumons tout ce que nous avons appris en appliquant le modificateur debugBorder3D.

    Voici le modificateur que j’ai montré plus tôt appliqué à un Model3D.

    Je définis une méthode debugBorder3D comme une extension sur View. J’applique une spatialOverlay au contenu modifié pour afficher la bordure dans le même espace 3D que la vue à laquelle elle est appliquée.

    Je place un ZStack à l’intérieur contenant une bordure 2D, un Spacer et une autre bordure 2D.

    Ensuite, j’applique une rotation3DLayout à l’ensemble du ZStack pour placer des bordures sur les faces avant et arrière de la vue.

    Enfin, je place ce ZStack interne à l’intérieur d’un autre ZStack avec des bordures 2D pour les faces arrière et avant. Ainsi, nous avons des bordures sur chaque côté.

    J’adore la façon dont je peux composer ces modificateurs SwiftUI 2D existants avec de nouvelles API 3D pour créer quelque chose de complètement nouveau.

    Il y a des analogues 3D pour la plupart des outils de disposition et des modificateurs que vous connaissez peut-être déjà dans un contexte 2D. Consultez la documentation pour en savoir plus sur ces API.

    SwiftUI est un excellent outil pour créer des apps en 3D, mais dans de nombreux cas d’utilisation, vous voudrez utiliser RealityKit, en mélangeant les deux dans la même app.

    Maintenant que votre contenu SwiftUI est en 3D, vous en aurez peut-être besoin pour interagir avec le code RealityKit. Mes amis Maks et Amanda ont construit des ajouts étonnants à BOT-anist en utilisant les deux frameworks ensemble. Consultez « Better togehter: SwiftUI and RealityKit » pour plus d’informations. J’ai hâte de voir à quoi ressemble votre app en 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)
            }
          }
        }
    • 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 ».

Developer Footer

  • Vidéos
  • WWDC25
  • Découvrez la disposition spatiale SwiftUI
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • App Store
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Fonts
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Open Source
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Sample Code
    • Tutorials
    • Downloads
    • Forums
    • Videos
    Open Menu Close Menu
    • Support Articles
    • Contact Us
    • Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Get the Apple Developer app.
    Copyright © 2025 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines