Modèles spatiaux visionOS au-delà de la fenêtre
La plupart des apps qui sortent sur visionOS atteignent la plateforme via le chemin de compatibilité « Designed for iPad » d’Apple : le binaire iPad existant s’exécute comme un panneau plat flottant dans l’espace 3D, et le développeur coche une case plutôt que de construire une expérience native visionOS. Le chemin convient à l’utilisateur (l’app fonctionne) mais il sous-vend la plateforme. La surface native de visionOS donne aux développeurs trois méthodes de présentation (Windows, Volumes et Immersive Spaces) ainsi que des primitives UI structurelles (Ornaments, Attachments) que l’SDK iPad ne possède pas. Les apps qui les adoptent semblent natives ; les apps qui ne le font pas se lisent comme du iPad-sur-Vision.
Cet article parcourt le vocabulaire spatial à la lumière de la documentation d’Apple. Le cadrage est « ce que la plateforme offre réellement à une app SwiftUI » plutôt qu’une introduction à visionOS. L’article du cluster RealityKit and the Spatial Mental Model couvre la couche de contenu 3D ; cet article-ci couvre la surface SwiftUI qui la contient.
TL;DR
- Les apps visionOS composent trois types de scènes :
WindowGroup(Windows),WindowGroupavec.windowStyle(.volumetric)(Volumes) etImmersiveSpace(Immersive Spaces)1. - Une Window est un plan 2D ; un Volume est une région 3D bornée ; un Immersive Space entoure l’utilisateur. Chacun a ses propres règles : les Volumes ont une taille immuable après création, les Immersive Spaces nécessitent une ouverture/fermeture explicite, les Windows se comportent le plus comme sur iPad.
- L’immersion existe en trois styles :
.mixed(le contenu coexiste avec la pièce),.full(la pièce est remplacée par un environnement virtuel),.progressive(un juste milieu avec ancrage périphérique)2. - Les Ornaments sont des plans UI parallèles à une Window et en avant sur l’axe z. C’est ainsi que visionOS gère les barres d’outils et les barres d’onglets3. Les Attachments intègrent des vues SwiftUI à l’intérieur du contenu 3D dans une RealityView, le pont entre l’UI plate et la géométrie spatiale.
- L’anti-modèle de l’« app panneau » : livrer l’UI iPad sous forme de Window sans adoption de Volume, Space ou Ornament. L’utilisateur peut utiliser l’app, mais la vraie valeur de la plateforme reste inexploitée.
Les trois types de scènes
Le corps App d’une app visionOS compose des scènes à partir de trois classes. Chacune correspond à un modèle mental utilisateur distinct.
Windows : le plan 2D
WindowGroup produit une Window 2D avec le cadre en verre visionOS par défaut. La Window est positionnée dans l’espace (le système la place devant le regard de l’utilisateur) et est déplacée ou redimensionnée par l’utilisateur via les gestes système standard. Du point de vue SwiftUI, une Window est l’analogue visionOS d’une fenêtre macOS : une surface de contenu plate avec un matériau verre conscient de la profondeur.
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
La Window par défaut a un matériau verre autour de son contenu. Les apps qui veulent une surface entièrement transparente utilisent .windowStyle(.plain) :
WindowGroup {
ContentView()
}
.windowStyle(.plain)
Les Windows de style plain perdent le cadre en verre du système. Utilisez-les lorsque le contenu fournit son propre conteneur visuel ; sinon, le défaut est correct.
Volumes : la région 3D bornée
Un Volume est une région 3D qui contient du contenu conscient de la profondeur (un modèle, une scène avec plusieurs objets, une UI qui bénéficie d’un troisième axe). La scène volume est également un WindowGroup, avec un style différent :
WindowGroup(id: "globe") {
GlobeView()
}
.windowStyle(.volumetric)
.defaultSize(width: 0.6, height: 0.6, depth: 0.6, in: .meters)
Le modificateur .defaultSize(width:height:depth:in:) spécifie les bornes du volume en unités du monde réel (mètres). Par défaut, les bornes sont fixées à l’ouverture, et l’utilisateur peut déplacer le volume mais pas le redimensionner. visionOS 2+ a ajouté un chemin opt-in via .windowResizability(.contentSize) et les APIs associées pour les apps qui veulent des volumes redimensionnables par l’utilisateur ; le défaut à taille fixe reste le cas le plus courant. L’implication : choisissez la taille par défaut avec soin, car la plupart des volumes ne sont pas redimensionnables sauf si le développeur opte explicitement pour cette option.
Les bons candidats pour les Volumes sont les apps où la limite spatiale fait partie de l’expérience : une sculpture virtuelle autour de laquelle l’utilisateur marche, un mètre ruban épinglé à un mur réel, une scène d’entraînement avec des cibles décalées en profondeur. Les apps qui veulent simplement une toile plus large ne gagnent rien d’un Volume ; une Window plus grande est la bonne réponse.
Immersive Spaces : l’enveloppement
Un ImmersiveSpace est une scène qui occupe l’environnement autour de l’utilisateur. Contrairement à une Window ou un Volume (tous deux visibles aux côtés d’autres apps dans le Shared Space), un Immersive Space prend le contrôle de l’environnement de l’utilisateur et bloque l’utilisation simultanée des fenêtres d’autres apps.
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
ImmersiveSpace(id: "training") {
TrainingScene()
}
.immersionStyle(selection: .constant(.mixed), in: .mixed, .progressive, .full)
}
}
Le modificateur .immersionStyle(...) choisit le niveau d’expérience :
.mixed. Le contenu virtuel apparaît aux côtés de la pièce réelle. Utilisé pour les apps où l’utilisateur bénéficie des deux contextes..progressive. Une immersion partielle qui utilise la Digital Crown pour augmenter ou diminuer. L’utilisateur conserve une conscience périphérique de la pièce pendant que la vue centrale est virtuelle..full. La pièce est remplacée par un environnement virtuel. Utilisé pour les expériences pleinement immersives (méditation, simulations d’entraînement, jeu).
L’ouverture d’un Immersive Space est explicite. L’app appelle @Environment(\.openImmersiveSpace) avec l’id de l’espace ; le système gère l’animation de transition et la fermeture de tout espace en conflit :
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
Button("Start Session") {
Task {
await openImmersiveSpace(id: "training")
}
}
Un seul Immersive Space peut être actif à la fois par app. La transition entre Spaces (de .mixed à .full, par exemple) requiert la fermeture explicite de l’ancien Space et l’ouverture du nouveau.
Ornaments : les plans UI autour d’une Window
Les Ornaments sont des vues SwiftUI attachées au bord d’une Window, positionnées légèrement en avant du plan de la Window sur l’axe z. C’est ainsi que visionOS gère les barres d’outils, les barres d’onglets et les contrôles accessoires. Le système utilise les ornaments partout : les contrôles de lecture dans TV, le contrôle segmenté dans Music, la barre d’outils dans Mail.
ContentView()
.ornament(
attachmentAnchor: .scene(.bottom),
contentAlignment: .center
) {
HStack {
Button("Previous", systemImage: "backward.fill") { ... }
Button("Play", systemImage: "play.fill") { ... }
Button("Next", systemImage: "forward.fill") { ... }
}
.padding()
.glassBackgroundEffect()
}
Le paramètre attachmentAnchor: spécifie où l’ornament se place par rapport à la Window : .scene(.top), .scene(.bottom), .scene(.leading), .scene(.trailing). Le traitement visuel de l’ornament est la responsabilité du développeur ; .glassBackgroundEffect() produit le matériau verre natif visionOS qui correspond au cadre de la Window.
Les ornaments résolvent un vrai problème sur visionOS : mettre les contrôles à l’intérieur de la Window encombre le contenu ; les mettre dans une Window séparée force l’utilisateur à recibler son regard. Un ornament flotte dans la vision périphérique de l’utilisateur, ciblable au regard, mais ne rivalise pas avec le contenu principal pour la vue centrale.
Attachments RealityView : SwiftUI à l’intérieur de l’espace 3D
Quand une app a besoin de vues SwiftUI à l’intérieur d’une scène 3D (une étiquette sur un modèle 3D, un bouton flottant près d’un objet virtuel, un affichage de mesure épinglé à une surface du monde réel), le pont est le mécanisme d’attachments de RealityView.
RealityView { content, attachments in
let model = ModelEntity(...)
content.add(model)
if let label = attachments.entity(for: "label") {
label.position = [0, 0.5, 0]
model.addChild(label)
}
} attachments: {
Attachment(id: "label") {
Text("Vintage Globe, 1872")
.padding()
.glassBackgroundEffect()
}
}
La closure attachments: déclare des vues SwiftUI avec des identifiants stables. À l’intérieur de la closure principale RealityView, attachments.entity(for:) récupère la vue comme un Entity 3D qui peut être positionné dans l’espace de coordonnées de la scène. La vue participe au cycle de mise à jour de SwiftUI (les changements d’état redessinent la vue) tout en étant rendue comme un plan texturé dans la scène 3D.
Le mécanisme est le bon pour toute UI dans le monde : une étiquette qui suit un objet en mouvement, une annotation de mesure, un bouton contextuel. La rédaction des vues SwiftUI reste inchangée ; le positionnement 3D se passe au niveau de la RealityView.
L’anti-modèle de l’« app panneau »
L’erreur de livraison la plus courante sur visionOS est l’app panneau : une app iPad qui arrive sur visionOS via la compatibilité « Designed for iPad » et est livrée comme une seule Window sans Volume, sans Immersive Space et sans Ornaments. L’app fonctionne, mais elle ne mérite pas la plateforme.
Trois signaux qu’une app est une app panneau :
Une seule scène Window. Pas de .windowStyle(.volumetric), pas d’ImmersiveSpace déclaré. L’app est une surface plate et c’est tout.
Aucun ornament adopté. La barre d’onglets de l’app vit à l’intérieur du contenu de la Window plutôt qu’à l’extérieur. Le résultat est plus encombré qu’une app native visionOS à la même densité de contenu.
Aucune fonctionnalité spatiale exclusive. L’app n’utilise pas le troisième axe pour quoi que ce soit : pas de modèles 3D dans un Volume, pas de scène environnementale dans un Space, pas d’UI positionnée en z via des attachments. L’app fait la même chose qu’elle faisait sur iPad, mais en flottant.
Les apps panneau ne sont pas des échecs ; c’est le bon choix pour les catégories de contenu qui ne bénéficient pas de l’informatique spatiale (une app de chat, une app de notes, un utilitaire de paramètres). Le mode d’échec est de livrer une app panneau et de revendiquer une autorité native visionOS pour elle. L’article du cluster Apple Platform Matrix défend que l’inclusion d’une plateforme est une décision produit ; pour visionOS, la décision est « cette app doit-elle mériter la surface spatiale, ou le panneau suffit-il ? ».
Échecs courants
Trois modèles qui produisent une mauvaise UX visionOS :
Des Volumes qui sont en réalité du contenu 2D avec du remplissage de profondeur. Une UI « 3D » qui remplit un Volume mais rend des plans plats à l’intérieur produit du gaspillage d’espace. Les Volumes sont pour le contenu 3D ; le contenu plat appartient à une Window.
Un style d’immersion qui combat le cas d’usage. Une app de méditation qui ne livre que l’immersion .full force l’utilisateur hors de son environnement pour de courtes sessions. Une app d’entraînement qui ne livre que .mixed ne va pas assez loin pour des exercices pleinement concentrés. Faites correspondre le style d’immersion à la session réelle de l’utilisateur.
Des ornaments qui rivalisent avec le contenu. Les ornaments sont périphériques par conception. Un ornament qui exige l’attention centrale (une couleur clignotante, un mouvement animé) en défait le but. Utilisez les ornaments pour des contrôles stables, perceptibles d’un coup d’œil.
Ce que ce modèle signifie pour les apps visionOS
Trois enseignements.
-
Choisissez le type de scène en fonction du modèle mental de l’utilisateur, pas de ce qui est facile. Une liste plate d’éléments est une Window. Un modèle 3D que l’utilisateur inspecte est un Volume. Un environnement enveloppant est un Immersive Space. Les mélanger dans une seule app (une Window avec un Volume ouvert à la demande, un Immersive Space accessible depuis le bouton d’une Window) est le modèle natif visionOS.
-
Adoptez les ornaments pour les barres d’outils et l’UI accessoire. Les ornaments sont la manière dont visionOS communique « cette UI est complémentaire » ; mettre les barres d’outils à l’intérieur du contenu de la Window se lit comme du iPad-sur-Vision. L’intégration est petite et la différence visuelle est grande.
-
Utilisez les attachments pour l’UI dans le monde dans RealityView. Étiquettes sur des objets 3D, boutons près de contenu virtuel, affichages contextuels. Le pont entre SwiftUI et l’espace 3D est résolu ; le mode d’échec consiste à ne pas l’utiliser et à finir avec un rendu de texte 3D ad hoc à la place.
Le cluster Apple Ecosystem complet : App Intents typés ; serveurs MCP ; la question du routage ; Foundation Models ; la distinction LLM runtime vs outillage ; trois surfaces ; le modèle source unique de vérité ; Two MCP Servers ; hooks pour le développement Apple ; Live Activities ; le contrat runtime watchOS ; internes SwiftUI ; le modèle mental spatial de RealityKit ; discipline de schéma SwiftData ; modèles Liquid Glass ; livraison multi-plateformes ; la matrice des plateformes ; framework Vision ; Symbol Effects ; inférence Core ML ; API Writing Tools ; Swift Testing ; Privacy Manifest ; l’accessibilité comme plateforme ; typographie SF Pro ; ce sur quoi je refuse d’écrire. Le hub se trouve à Apple Ecosystem Series. Pour un contexte iOS-avec-agents-IA plus large, voir le guide iOS Agent Development.
FAQ
Quelle est la différence entre un Volume et un Immersive Space ?
Un Volume est une région 3D bornée qui vit dans le Shared Space aux côtés d’autres apps. L’utilisateur peut marcher autour, le système l’encadre, et les Windows des autres apps restent visibles. Un Immersive Space entoure l’utilisateur, prend le contrôle de l’environnement et empêche l’utilisation simultanée d’autres apps. Les Volumes sont pour « regardez cette chose 3D » ; les Spaces sont pour « soyez dans cet environnement ».
Puis-je ouvrir plusieurs Volumes en même temps ?
Oui. Plusieurs scènes WindowGroup-avec-.volumetric peuvent être ouvertes simultanément, chacune avec sa propre taille et son propre contenu. Le système les positionne indépendamment dans l’espace.
Puis-je ouvrir plusieurs Immersive Spaces en même temps ?
Non. Un seul Immersive Space peut être actif par app à la fois. Basculer entre Spaces requiert la fermeture explicite du Space actuel et l’ouverture du nouveau via @Environment(\.openImmersiveSpace) et @Environment(\.dismissImmersiveSpace).
La taille d’un Volume est-elle vraiment immuable ?
Les bornes d’un Volume sont fixées à l’ouverture par défaut ; le cadrage du HIG visionOS est que les Volumes représentent du contenu 3D spécifique avec des bornes intentionnelles, et qu’un redimensionnement utilisateur arbitraire déformerait l’échelle voulue du contenu. visionOS 2+ a ajouté un opt-in développeur pour les volumes redimensionnables via .windowResizability(.contentSize) et les APIs associées, ainsi les apps qui ont besoin de conteneurs spatiaux redimensionnables par l’utilisateur peuvent le demander. La plupart des volumes sont livrés avec le défaut fixe, ce que le HIG continue de recommander pour le contenu à échelle spécifique (une sculpture virtuelle, un modèle à taille physique).
Comment ajouter une barre d’onglets à une Window visionOS ?
Utilisez une TabView à l’intérieur de la Window pour des onglets dans le contenu (le modèle de style iPad), ou utilisez un ornament avec des rangées de boutons personnalisées pour une UI d’onglets périphérique native visionOS. Le chemin ornament est ce que les propres apps d’Apple utilisent (Music, Mail) et ce qui semble le plus natif aux utilisateurs visionOS.
Les attachments RealityView peuvent-ils interagir avec le suivi des mains ?
Oui. Les attachments sont des entités 3D une fois positionnés, et ils participent au même système de gestes et de hit-testing que les autres entités RealityKit. Les gestes tap, drag et hover s’y attachent via les modificateurs de gestes standard de SwiftUI ; l’article RealityKit du cluster couvre les modèles d’intégration du suivi des mains.
Références
-
Apple Developer : Meet SwiftUI for spatial computing (session WWDC 2023 10109). Introduction de WindowGroup, du WindowGroup volumétrique et d’ImmersiveSpace comme les trois types de scènes visionOS. ↩
-
Documentation Apple Developer :
ImmersionStyle. Les trois styles d’immersion (.mixed,.progressive,.full) et l’API du modificateur.immersionStyle(selection:in:). ↩ -
Documentation Apple Developer :
ornament(visibility:attachmentAnchor:contentAlignment:ornament:). Le modificateur de vue SwiftUI qui ajoute un plan UI ornament à une Window avec l’ancre spécifiée. ↩ -
Apple Developer : Go beyond the window with SwiftUI (session WWDC 2023 10111). La session couvrant les Volumes, les Immersive Spaces et les modèles pour aller au-delà de l’UI à panneau plat sur visionOS. ↩
-
Documentation Apple Developer : Creating an immersive space in visionOS with SwiftUI. Le guide de bout en bout pour définir et ouvrir des espaces immersifs. ↩