visionOS es el sistema operativo de Apple Vision Pro, disponible desde 2024. Combina SwiftUI con RealityKit para crear experiencias espaciales que mezclan objetos 3D con la interfaz 2D habitual de una app. La curva de aprendizaje es menor de lo que parece: la mayor parte de SwiftUI funciona igual, y el espacio 3D se añade de forma incremental.
El modelo de espacio en visionOS
visionOS define tres modos de experiencia:
- Window: ventana 2D flotante, idéntica a una app macOS o iOS.
- Volume: caja 3D con contenido RealityKit visible desde todos los ángulos.
- Immersive Space: el usuario entra en un espacio que puede ocupar todo el campo de visión.
import SwiftUI
import RealityKit
@main
struct MiAppVision: App {
var body: some Scene {
// Ventana estándar
WindowGroup {
ContentView()
}
// Volumen 3D
WindowGroup(id: "cubo3d") {
VistaVolumen()
}
.windowStyle(.volumetric)
.defaultSize(width: 0.4, height: 0.4, depth: 0.4, in: .meters)
// Espacio inmersivo
ImmersiveSpace(id: "espacio") {
EspacioInmersivo()
}
.immersionStyle(selection: .constant(.mixed), in: .mixed, .progressive, .full)
}
}
RealityView: objetos 3D en SwiftUI
RealityView es el puente entre SwiftUI y RealityKit. Permite crear y actualizar entidades 3D como si fueran vistas SwiftUI:
struct VistaEsfera: View {
var body: some View {
RealityView { contenido in
// Crear una esfera
let mesh = MeshResource.generateSphere(radius: 0.1)
let material = SimpleMaterial(color: .blue, isMetallic: true)
let esfera = ModelEntity(mesh: mesh, materials: [material])
esfera.position = [0, 0, -0.5] // 50 cm frente al usuario
contenido.add(esfera)
} update: { contenido in
// Actualizar cuando cambia el estado de SwiftUI
}
.gesture(
TapGesture()
.targetedToAnyEntity()
.onEnded { valor in
// Manejar toque en cualquier entidad
valor.entity.scale *= 1.2
}
)
}
}
Cargar modelos USDZ
Los modelos 3D en visionOS usan el formato USDZ. Se pueden cargar desde el bundle o desde la red:
RealityView { contenido in
// Cargar modelo del bundle
if let modelo = try? await ModelEntity(named: "silla.usdz") {
modelo.position = [0, 0, -1.0]
modelo.scale = [0.5, 0.5, 0.5]
// Añadir componente para gestos
modelo.components.set(InputTargetComponent())
modelo.generateCollisionShapes(recursive: true)
contenido.add(modelo)
}
}
ImmersiveSpace: experiencias inmersivas
Un ImmersiveSpace puede estar en tres modos: .mixed (objetos 3D mezclados con el entorno real), .progressive (el usuario controla cuánto ocluye el entorno) y .full (entorno completamente virtual):
struct EspacioInmersivo: View {
var body: some View {
RealityView { contenido in
// Suelo virtual
let suelo = ModelEntity(
mesh: .generatePlane(width: 10, depth: 10),
materials: [SimpleMaterial(color: .init(white: 0.1, alpha: 0.5), isMetallic: false)]
)
suelo.position = [0, -1, 0]
contenido.add(suelo)
// Partículas o elementos del entorno
for i in 0..<20 {
let bola = ModelEntity(
mesh: .generateSphere(radius: 0.05),
materials: [SimpleMaterial(color: .random, isMetallic: true)]
)
bola.position = [Float.random(in: -3...3), Float.random(in: 0...2), Float.random(in: -3...0)]
contenido.add(bola)
}
}
}
}
// Abrir el espacio inmersivo desde una ventana
struct ContentView: View {
@Environment(.openImmersiveSpace) var openImmersiveSpace
@Environment(.dismissImmersiveSpace) var dismissImmersiveSpace
var body: some View {
Button("Entrar en el espacio") {
Task { await openImmersiveSpace(id: "espacio") }
}
}
}
Attachments: mezclar SwiftUI con RealityKit
Los attachments permiten anclar vistas SwiftUI a entidades de RealityKit, muy útil para etiquetas, menús contextuales o paneles de información flotantes:
RealityView { contenido, attachments in
let planeta = ModelEntity(mesh: .generateSphere(radius: 0.15),
materials: [SimpleMaterial(color: .blue, isMetallic: false)])
planeta.name = "tierra"
planeta.position = [0, 0.5, -1]
contenido.add(planeta)
// Adjuntar la etiqueta SwiftUI al planeta
if let etiqueta = attachments.entity(for: "etiqueta-tierra") {
etiqueta.position = [0, 0.2, 0]
planeta.addChild(etiqueta)
}
} attachments: {
Attachment(id: "etiqueta-tierra") {
VStack {
Text("Tierra")
.font(.title2.bold())
Text("149,6 M km del Sol")
.font(.caption)
}
.padding(8)
.background(.ultraThinMaterial)
.cornerRadius(8)
}
}
Ornaments: controles flotantes
Los ornaments son paneles SwiftUI que flotan alrededor de las ventanas de la app, sin ocupar espacio dentro de la ventana:
struct ContentView: View {
var body: some View {
Lista3D()
.ornament(attachmentAnchor: .scene(.bottom)) {
HStack {
Button("Añadir") { }
Button("Ordenar") { }
}
.padding()
.glassBackgroundEffect()
}
}
}
Resumen
visionOS extiende SwiftUI con tres conceptos clave: RealityView para integrar objetos 3D de RealityKit en la jerarquía de vistas; ImmersiveSpace para experiencias que van más allá de una ventana flotante; y los attachments y ornaments para mezclar vistas SwiftUI con el espacio 3D. La base de conocimiento es SwiftUI y RealityKit, frameworks que ya existían; visionOS añade la capa espacial sin reinventar la rueda.
