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

Videos

Abrir menú Cerrar menú
  • Colecciones
  • Temas
  • Todos los videos
  • Información

Más videos

  • Información
  • Resumen
  • Transcripción
  • Código
  • Mejor juntos: SwiftUI y RealityKit

    Descubre cómo combinar sin problemas SwiftUI y RealityKit en visionOS 26. Exploraremos las mejoras a Model3D, incluida la animación y la compatibilidad con ConfigurationCatalog, y demostraremos transiciones fluidas a RealityView. Aprenderás a aprovechar las animaciones de SwiftUI para impulsar cambios en los componentes de RealityKit, implementar manipulación interactiva, usar nuevos componentes de SwiftUI para interacciones más enriquecidas y observar los cambios de RealityKit desde tu código de SwiftUI. También hablaremos sobre cómo usar la conversión de coordenadas unificada para transformaciones de coordenadas entre estructuras.

    Capítulos

    • 0:00 - Introducción
    • 1:24 - Mejoras a Model3D
    • 6:13 - Transición de RealityView
    • 11:52 - Manipulación de objetos
    • 15:35 - Componentes de SwiftUI
    • 19:08 - Flujo de información
    • 24:56 - Conversión de coordenadas unificada
    • 27:01 - Animación
    • 29:41 - Próximos pasos

    Recursos

    • Canyon Crosser: Building a volumetric hike-planning app
    • Rendering hover effects in Metal immersive apps
      • Video HD
      • Video SD

    Videos relacionados

    WWDC24

    • Compose interactive 3D content in Reality Composer Pro

    WWDC23

    • Discover Observation in SwiftUI

    WWDC21

    • Dive into RealityKit 2

    WWDC20

    • Data Essentials in SwiftUI
  • Buscar este video…

    Hola. Soy Amanda, ingeniera de RealityKit. Y yo Maks. Soy ingeniero de SwiftUI. Hoy compartiremos algunas mejoras fantásticas de SwiftUI y RealityKit que les ayudarán a trabajar mejor en conjunto. Mira esta adorable escena. Hay un encantador robot SwiftUI flotando en el aire y un robot RealityKit parado en una superficie esperando conexión. Al acercarse, saltan chispas. ¿Cómo pueden acercarse lo suficiente para una interacción real? Maks y yo compartiremos cómo combinar la IU tradicional y el contenido 3D interactivo. Primero, mostraré las mejoras de Model3D. Luego, demostraré cómo pasar del uso de Model3D al uso de RealityView y hablaré sobre cuándo elegir uno u otro. Hablaremos de la nueva API Object Manipulation.

    RealityKit tiene nuevos componentes e integra más aspectos de SwiftUI. La información ya puede fluir entre SwiftUI y RealityKit. Lo explicaremos.

    La conversión del espacio de coordenadas es más sencillo. Cambia los componentes de RealityKit con animaciones SwiftUI. ¡Hagámoslo!

    Muestra modelos 3D en tus apps con una línea de código usando Model3D. En visionOS 26, dos mejoras te permiten hacer aún más con Model3D: reproducir animaciones y cargar desde ConfigurationCatalog. Dado que Model3D es una vista SwiftUI, forma parte del sistema SwiftUI. Lo usaré para hacer un pequeño texto que muestre el nombre del robot.

    El texto dice que su nombre es Sparky.

    Sparky también tiene movimientos de baile. El artista combinó esta animación con el modelo de robot. Una novedad de visionOS 26 es Model3DAsset. Carga y controla animaciones en tu contenido 3D construyendo un Model3D con Model3DAsset. El modelo carga las animaciones del activo y te permite elegir cuál reproducir. "Modelo" tiene muchas acepciones, sobre todo en esta sesión donde convergiremos una estructura de IU y uno de juego 3D. En estructuras de UI, un "modelo" es la estructura de datos que representa la información que usa tu app. El modelo tiene los datos y la lógica de negocios, permitiendo que la vista muestre esa información. En estructuras 3D como RealityKit, un modelo es un objeto 3D que se puede poner en una escena. Se accede a ella a través de ModelComponent, que consta de un recurso de malla que define su forma y materiales que determinan su apariencia. A veces eso pasa. Dos mundos chocan, trayendo consigo sus terminologías y a veces hay superposiciones. Volvamos a Sparky y su animación.

    Colocaré el Model3D encima de un Selector, un botón Reproducir y una barra de tiempo. En mi RobotView, mostraré el robot, y debajo de él, colocaré un Selector para elegir qué animación reproducir, además de los controles de reproducción. Primero, inicializaré un Model3DAsset con el nombre de la escena para cargar desde mi paquete. Luego, una vez que tengo el activo, lo paso al inicializador Model3D. Debajo, en VStack, presento un Selector personalizado que enumera las animaciones disponibles en este activo de modelo. Al elegir un elemento de la lista, el Selector establece selectedAnimation en el nuevo valor. Luego, Model3DAsset crea un AnimationPlaybackController para controlar la reproducción de la animación. El activo otorga animationPlaybackController. Este objeto permite pausar, reanudar y buscar en la animación.

    Pasaré ese animationController a mi vista RobotAnimationControls, que también analizaremos en breve. En visionOS 26, la clase RealityKit existente, AnimationPlaybackController, ahora es observable. En mi vista de SwiftUI, la propiedad "time" permite ver el progreso de la animación.

    Tengo una propiedad @Bindable llamada "controller", eso significa que AnimationPlaybackController es el modelo de datos de mi vista. Cuando el valor isPlaying del controlador cambia, SwiftUI reevaluará mi vista RobotAnimationControls. Tengo una barra que muestra el tiempo actual en la animación, en relación con la duración total de esta. Puedes arrastrar esta barra que permite manipular la animación. Aquí está Sparky con su animación de celebración. Puedo adelantar o retroceder rápidamente con la barra. ¡Vamos Sparky, es tu cumpleaños! Sparky ya aprendió unos movimientos y quiere disfrazarse antes de ir al invernadero donde se encontrará con otro robot. Puedo ayudarlo haciendo mejoras en el tipo ConfigurationCatalog de RealityKit. Este tipo almacena representaciones alternativas de una entidad: geometrías de malla, valores de componentes o propiedades de materiales. En visionOS 26, puedes inicializar un Model3D con ConfigurationCatalog y cambiar entre distintas representaciones.

    Para que Sparky se pruebe varios outfits, mi artista combinó un archivo de realidad con varios tipos de cuerpo. Cargaré este archivo como ConfigurationCatalog desde mi paquete principal. Crearé mi Model3D con la configuración. Este menú tiene opciones de configuración. Las opciones del menú cambian el aspecto de Sparky. ¿Pasos de baile? Listo. ¿Outfit? Listo. Sparky está listo para conocer a su nuevo amigo en el invernadero RealityKit. ¡Van a saltar chispas! Usaré un emisor de partículas para hcerlo. Pero eso no es algo que pueda hacer durante la ejecución con Model3D. Debo agregar el emisor de partículas a una entidad RealityKit. Pasaremos a eso en un momento. Debo destacar que Model3D no permite agregar componentes. Así que, para agregar un emisor, cambiaré a RealityView. Compartiré cómo reemplazar mi Model3D con un RealityView sin cambiar el diseño. Cambio la vista de Model3D a RealityView. Cargo el modelo botánico del paquete de la app dentro del cierre "make" de RealityView, creando una entidad. Agrego esta al contenido de RealityView para que Sparky aparezca en la pantalla.

    Pero ahora el nombre se desplazó mucho hacia un lado. Esto no ocurría cuando usábamos Model3D. Sucede ahora porque, por defecto, RealityView ocupa todo el espacio disponible que le otorga el sistema de diseño SwiftUI. Por el contrario, el tamaño de Model3D se ajusta al tamaño del archivo del modelo subyacente. ¡Lo puedo arreglar! Aplico el nuevo modificador .realityViewLayoutBehavior con .fixedSize para que RealityView se ajuste a los límites iniciales del modelo. Mucho mejor. RealityView usará los límites visuales de las entidades en su contenido para determinar su tamaño. Este tamaño solo se evalúa una vez: después de ejecutar el cierre "make". Las otras opciones para .realityViewLayoutBehavior son .flexible y .centered. En las tres RealityViews, tengo la parte inferior del modelo Sparky sobre el origen de la escena y marqué ese origen con una pequeña cruz multicolor que muestra los ejes y el origen. A la izquierda, con .flexible, RealityView actúa como si el modificador no se hubiera aplicado. El origen está en el centro de la vista. La opción .centered mueve el origen de RealityView para que el contenido quede centrado en la vista. .fixedSize hace que RealityView se ajuste a los límites del contenido y que se comporte exactamente como Model3D.

    Ninguna de estas opciones reposiciona o escala tus entidades con respecto a RealityViewContent: solo reposicionan el punto de origen de RealityView. Ya arreglé el tamaño de Sparky en RealityView. Ahora, le regresaré la animación. Pasaré de la nueva API de animación de Model3D a la de RealityKit directamente en la entidad. Para conocer más sobre las formas de trabajar con animación en RealityKit, consulta la sesión "Componer contenido 3D interactivo en Reality Composer Pro". Cambié de Model3D a RealityView para poder darle a Sparky un ParticleEmitterComponent, porque las chispas deben saltar cuando estos dos robots se acerquen. Los emisores de partículas crean efectos que involucran cientos de partículas animadas al mismo tiempo, como fuegos artificiales, lluvia y destellos. RealityKit les proporciona valores por defecto y puedes ajustarlos para obtener el efecto que buscas. Puedes usar Reality Composer Pro para diseñarlos y configurarlos en código. Agrega ParticleEmitter a una entidad como un componente. Los componentes son parte central de RealityKit, que se basa en el paradigma Entity Component System. Cada objeto de tu escena es una entidad y le agregas componentes para indicarle rasgos y comportamientos. Un componente es el tipo que guarda datos de una entidad. Un sistema procesa entidades con componentes específicos, realizando lógica que involucra esos datos. Hay sistemas integrados para cosas como animar partículas, manejar la física, renderizar y mucho más. Escribe tu sistema personalizado en RealityKit y crea una lógica personalizada para tu juego o app. Ve "Profundizar en RealityKit 2" para explorar el tema Entity Component System en RealityKit. Agregaré un emisor de partículas a cada lado de la cabeza de Sparky. Primero hago dos entidades invisibles que serán los contenedores para el efecto de chispas. Diseñé mi emisor para que apunte hacia la derecha. Lo agregaré a mi entidad invisible en el lado derecho de Sparky.

    En el otro lado, giro la entidad 180 grados sobre el eje "y" para que apunte hacia la izquierda.

    Al ensamblar todo en RealityView, aquí está la animación de Sparky, el texto de su nombre en la posición correcta y las chispas.

    RealityKit es ideal para creaciones detalladas. Si vas a crear un juego o una experiencia de juego, o necesita un control detallado sobre el comportamiento de tu contenido 3D, elige RealityView. Por otro lado, usa Model3D para mostrar un activo 3D autónomo por sí solo. Piensa que es una vista de imagen de SwiftUI, pero para activos 3D.

    Puedes hacer más con los nuevos catálogos de animación y configuración de Model3D. Si tu diseño evoluciona y necesitas acceso directo a las entidades, componentes y sistemas, cambia sin problemas de Model3D a RealityView usando realityViewLayoutBehavior. Ahora, compartiré detalles sobre la nueva API Object Manipulation en visionOS 26, que permite tomar los objetos virtuales en tu app. Object manipulation funciona desde SwiftUI y desde RealityKit. Con la manipulación de objetos, puedes mover el objeto con una sola mano, girarlo con una o ambas manos y escalarlo estirándolo con ambas manos. Incluso puedes pasar el objeto de una mano a la otra.

    Hay dos formas de habilitarla, dependiendo de si el objeto es una entidad RealityKit o una vista SwiftUI. En SwiftUI, agrega el modificador .manipulable. Para impedir el escalado, pero conservar la capacidad de mover y rotar el robot con las manos, especifico las operaciones que se admiten.

    Para que se sienta superpesado, especifico que tiene alta inercia.

    El modificador .manipulable funciona cuando Sparky aparece en una vista Model3D. Se aplica a todo el Model3D o a cualquier vista a la que esté asociado. Cuando Sparky está en RealityView, debo habilitar la manipulación solo en la entidad del robot en sí, no en todo. En visionOS 26, puedes configurar el tipo ManipulationComponent para habilitar la manipulación de objetos. La función estática configureEntity agrega un ManipulationComponent a tu entidad. También agrega un CollisionComponent para que el sistema de interacción sepa cuándo tocaste esa entidad. Agrega un InputTargetComponent, que le dice al sistema que la entidad responde a los gestos. Y finalmente, agrega un HoverEffectComponent, que aplica un efecto visual cuando una persona lo mira o pasa el mouse encima. Solo necesitas esta línea para habilitar la manipulación de una entidad en tu escena. Para personalizar más la experiencia, hay varios parámetros que puedes pasar. Especifico un efecto de resaltado morado. Permito todo tipo de entradas: tacto directo, mirada indirecta y pellizco. Y proporciono formas de colisión que definen las dimensiones externas del robot. Para responder cuando una persona interactúa con un objeto en tu app, el sistema de manipulación de objetos genera eventos en momentos clave, como cuando la interacción inicia y se detiene, se actualiza cuando la entidad se mueve, gira y escala, cuando se libera y cuando pasa entre las manos. Suscríbete a estos eventos y actualiza tu estado. Por defecto, los sonidos estándar se reproducen cuando la interacción comienza, el objeto cambia de mano o se libera. Para aplicar sonidos personalizados, configuro audioConfiguration en "ninguno". Esto desactiva los sonidos estándar. Luego me suscribo a ManipulationEvent DidHandOff, que se entrega cuando una persona pasa el robot de una mano a otra. En ese cierre, toco mi recurso de audio. Bueno, Maks. El viaje de Sparky fue emocionante: animar en Model3D, encontrar su nuevo hogar en RealityView, mostrar su personalidad con chispas y permitir que las personas interactúen con él. Logró un gran progreso en su camino al invernadero RealityKit. ¡Seguro! Pero para que Sparky pueda conectar con el robot que lo espera, los objetos a su alrededor necesitan nuevas capacidades. Necesitan responder a gestos, presentar información sobre sí mismos y activar acciones de una forma que parezca nativa de SwiftUI.

    El recorrido de Sparky al invernadero RealityKit se trata de construir conexiones. La conexión profunda requiere interacción enriquecedora. Eso es exactamente lo que los nuevos componentes SwiftUI RealityKit permiten. Estos componentes de visionOS 26 aportan capacidades SwiftUI avanzadas y familiares a las entidades RealityKit. RealityKit tiene tres componentes clave: ViewAttachmentComponent, que permite agregar vistas SwiftUI directamente a tus entidades. GestureComponent, que hace que tus entidades respondan al tacto y a los gestos. Y PresentationComponent, que presenta vistas de SwiftUI, como ventanas emergentes, desde dentro de la escena RealityKit.

    visionOS 1 permite declarar archivos adjuntos como parte del inicializador RealityView. Tras evaluar tu generador de vistas de archivos, el sistema llamó al cierre de actualización con los resultados como entidades. Puedes agregar estas a tu escena y posicionarlas en un espacio 3D. En visionOS 26, esto se simplifica. Ahora puedes crear archivos adjuntos usando un componente RealityKit desde tu app. Crea tu ViewAttachmentComponent asignándole cualquier vista SwiftUI. Luego, agrégalo a la colección de componentes de una entidad.

    Y así, moví el texto del nombre de SwiftUI a RealityKit. ¡Exploremos los gestos! Ya puedes adjuntar gestos a tu RealityView con modificadores de gestos targetedToEntity. GestureComponent es nuevo en visionOS 26. Al igual que ViewAttachmentComponent, agrega GestureComponent a tus entidades, pasándole los gestos SwiftUI regulares. Los valores del gesto se informan por defecto en el espacio de coordenadas. ¡Muy práctico! Uso GestureComponent con un TapGesture para activar y desactivar el nombre.

    Mira. El nombre de este robot es Bolts.

    Consejo: en cualquier entidad que sea el objetivo de un gesto, agrega también InputTargetComponent y CollisionComponent. Esto aplica tanto a GestureComponent como a la API de gestos de destino.

    GestureComponent y ViewAttachmentComponent me permiten crear un texto con el nombre de Bolts. Pero Bolts debe prepararse para un visitante especial: ¡Sparky! Bolts quiere lucir lo mejor posible para su reunión en el invernadero. ¡Hora de otro cambio de outfit! Reemplazaré el texto con IU para elegir lo que usará Bolts. Una decisión trascendental.

    Para enfatizar esto, mostraré esta IU en una ventana emergente, usando PresentationComponent desde RealityKit.

    Primero, reemplazo ViewAttachmentComponent con PresentationComponent. El componente toma un enlace booleano para controlar cuándo aparece la ventana emergente y para notificarte cuando alguien la cierra. El parámetro "configuration" es el tipo de presentación que se verá. Especificaré "ventana emergente". En la ventana, presentaré una vista con opciones de catálogo de configuración para vestir a Bolts. Ahora, puedo ayudar a Bolts a elegir su mejor color para cuando Sparky llegue.

    Oye Maks, ¿crees que Bolts es más de verano? ¿O de otoño?

    Es una broma de moda.

    Bolts se vistió para impresionar. Pero, tiene trabajo que hacer. Bolts riega las plantas en el invernadero. Haré un minimapa, como en una pantalla de visualización frontal, para saber en dónde está Bolts. Para eso, debo observar el componente Transform del robot. En visionOS 26, las entidades se pueden ver. Pueden notificar otro código cuando sus propiedades cambien. Para recibir una notificación, lee la propiedad “observable”.

    Desde esta propiedad puedes observar cambios en la posición, escala y rotación de la entidad, su colección secundaria y componentes, ¡incluso tus propios componentes personalizados! Observa estas propiedades usando un bloque withObservationTracking. O apóyate en el seguimiento de observación integrado de SwiftUI. Usaré SwiftUI para aplicar mi minimapa. Para conocer más sobre la observación, ve "Descubre la observación en SwiftUI". En esta vista, muestro la posición de mi entidad en un minimapa. Accedo a este valor observable en mi entidad. Eso le dice a SwiftUI que mi vista depende de ese valor.

    Cuando Bolts se mueva por el invernadero, regando las plantas, su posición cambiará. Cada vez que lo haga, SwiftUI llamará al cuerpo de mi vista, moviendo su símbolo igual en el minimapa. Para ver una explicación más detalla del flujo de datos de SwiftUI, consulta "Fundamentos de datos en SwiftUI".

    Falta poco para que nuestros robots se reúnan. Es lo ideal. Me gustó tu descripción anterior de la diferencia entre "modelo" y "modelo". Y a veces necesitas pasar datos de tu modelo de datos a tu modelo de objetos 3D, y viceversa. En visionOS 26, las entidades observables nos dan una herramienta para eso. Desde el principio, podías pasar información de SwiftUI a RealityKit en el cierre "update" de RealityView. Ahora, con la propiedad Entity: Observable, puedes enviar información de regreso. Las entidades RealityKit actúan como objetos modelo para actualizar tus vistas SwiftUI. Ahora la información podrá fluir en ambas direcciones: de SwiftUI a RealityKit y de RealityKit a SwiftUI. Pero... ¿esto crea el potencial para un bucle infinito? ¡Sí! Veamos cómo evitar la creación de bucles infinitos entre SwiftUI y RealityKit. Cuando lees una propiedad "observable" dentro del cuerpo de una vista, creas una dependencia: tu vista depende de esa propiedad. Cuando el valor de la propiedad cambia, SwiftUI actualiza la vista y vuelve a ejecutar su cuerpo. RealityView tiene un comportamiento especial. Piensa que le cierre "update" es una extensión del cuerpo de la vista contenedora.

    SwiftUI llamará al cierre siempre que cambie el estado de esa vista, no solo cuando cambie el estado que se observa explícitamente en el cierre. Aquí, en el cierre "update" de mi RealityView, cambiaré esa posición. Esto escribirá en el valor de la posición, lo que hará que SwiftUI actualice la vista y vuelva a ejecutar su cuerpo, provocando un bucle infinito.

    Para evitar crear bucles infinitos, no modifiques el estado observado dentro del cierre de actualización.

    Puedes cambiar entidades que no observes. Eso no creará un bucle infinito porque sus cambios no harán que SwiftUI vuelva a evaluar el cuerpo de la vista. Si necesitas modificar una propiedad observada, verifica el valor existente de esa propiedad y evita reescribir ese mismo valor. Rompe el ciclo y evita un bucle infinito. Considera que el cierre "make" de RealityView es especial. Cuando accedes a una propiedad observable en el cierre "make", eso no crea una dependencia. No se incluye en el alcance de observación de la vista contenedora. Además, el cierre 'make' no se vuelve a ejecutar al hacer cambios. Solo se ejecuta cuando aparece la vista contenedora primero. También puedes actualizar las propiedades de una entidad observada desde tu propio sistema personalizado. La actualización de un sistema no se incluye en la evaluación del cuerpo de la vista de SwiftUI, por lo que es excelente para cambiar los valores de mis entidades.

    Los cierres de gestos tampoco se incluyen en la evaluación del cuerpo de la vista de SwiftUI. Se llaman en respuesta a la entrada del usuario. También puedes modificar aquí los valores de las entidades. En resumen, algunos lugares son mejores para modificar las entidades observadas.

    Si descubres un bucle infinito en tu app, aquí te damos un consejo para solucionarlo: divide tus vistas más grandes en vistas pequeñas e independientes, cada una con su propio estado necesario. Así, si cambias una entidad no relacionada, no harás que tu pequeña vista sea reevaluada. ¡Esto también aplica para el rendimiento! Sabes, Maks, es posible que descubras que ya no necesitas usar tu cierre "update". Dado que tu entidad ahora puede ser el estado de tu vista, modificarla en los lugares donde acostumbras cambiar el estado y renunciar por completo al cierre "update". ¡Sí! Siento que evitar bucles infinitos es algo que tengo que aprender una y otra vez. Pero, si no uso un cierre "update", es menos probable que me tope con uno. Creo que ya podemos reunir a Bolts y Sparky. Bolts por fin terminó su trabajo: ¡es hora de su cita con Sparky! Mientras levanto a Sparky y los dos robots se acercan, quiero hacer que salten chispas en función de la distancia decreciente entre ellos. Usaré nuestra nueva API Unified Coordinate Conversion para lograrlo. Sparky está en una vista SwiftUI de Model3D y Bolts es una entidad en el invernadero de RealityKit. Necesito obtener la distancia total entre estos dos robots, aunque estén en diferentes espacios de coordenadas. Para resolverlo, el marco espacial ahora define un protocolo CoordinateSpace3D que representa un espacio de coordenadas abstracto. Puedes convertir fácilmente valores entre dos tipos que se adapten a CoordinateSpace3D, incluso desde marcos diferentes. Los tipos Entity y Scene de RealityKit se ajustan a CoordinateSpace3D. En SwiftUI, GeometryProxy3D tiene una nueva función .coordinateSpace3D que te da su espacio de coordenadas. Además, varios tipos de gestos pueden proporcionar sus valores relativos a cualquier CoordinateSpace3D. El protocolo CoordinateSpace3D convierte primero un valor en el espacio de coordenadas de Sparky a uno en el espacio compartido por RealityKit y SwiftUI. Luego, convierte del espacio compartido al espacio de coordenadas de Bolt, considerando detalles de bajo nivel como la conversión de puntos a metros y la dirección del eje. En la vista Model3D de Sparky, cada vez que cambia la geometría de la vista, el sistema llama a onGeometryChange3D. Pasa un GeometryProxy3D que uso para obtener su espacio de coordenadas. Luego, puedo convertir la posición de mi vista en un punto del espacio de la entidad para saber la distancia entre mis dos robots. Ahora, cuando Amanda reúne a Bolts y Sparky, las chispas aumentan. Las chispas disminuyen cuando los separa.

    Le enseñaré a estos robots a moverse juntos y a coordinar sus acciones. Usaré animación de SwiftUI para los componentes de RealityKit. SwiftUI ya viene con excelentes API para animar implícitamente los cambios en las propiedades de tu vista. Aquí, animo la vista Model3D, donde está Sparky. Lo envío hacia la izquierda cuando lo alterno y luego rebota a la posición original cuando lo alterno de nuevo. Agregaré una animación a mi enlace isOffset y especificaré que quiero una animación de rebote adicional. En visionOS 26, ya puedes usar SwiftUI para animar implícitamente los cambios en los componentes de RealityKit. Solo necesitas configurar un componente compatible con tu entidad dentro de un bloque de animación RealityKit y el marco hará el resto. Hay dos formas de asociar una animación con un cambio de estado. Desde RealityView, puedes usar content.animate para establecer nuevos valores a tus componentes dentro del bloque animado. RealityKit usará la animación asociada con la transacción SwiftUI que activó el cierre "update", que, en este caso, es una animación de rebote adicional.

    La otra forma de llamar a la nueva función Entity.animate(), pasando una animación SwiftUI y un cierre que establezca los nuevos valores de tus componentes. Siempre que la propiedad isOffset cambie, Sparky irá a la izquierda o derecha con la posición de entidad. Al establecerla en el bloque animado empezará una animación implícita del componente Transform, haciendo que la entidad se mueva fácilmente a la nueva posición. El poder de la animación implícita realmente brilla cuando la combino con la API Object Manipulation que presentó Amanda. Puedo usar una animación SwiftUI para implementar un release.Behavior personalizado para Bolts. Primero voy a deshabilitar el release.Behavior por defecto para la manipulación de objetos configurándolo en .stay. Luego, me suscribiré al evento WillRelease para la interacción de manipulación. Y cuando el objeto esté a punto de ser liberado, regresaré a Sparky estableciendo su transformación en identidad, lo que restablece la escala, traducción y rotación de la entidad. Como modifiqué la transformación de Sparky dentro del bloque animado, volverá a su posición por defecto. Ahora la animación de Sparky en su posición original es más divertida. Todos estos componentes integrados de RealityKit admiten animaciones implícitas, incluidos los componentes Transform, Audio y Model, así como Light, que tiene propiedades de color. Sparky y Bolts tuvieron un buen recorrido. Es fantástico ver el poder de SwiftUI y RealityKit trabajando juntos. Con esta conexión, también podrás desarrollar apps espaciales verdaderamente excepcionales, fomentando una conexión real entre lo virtual y lo físico. Imagina las posibilidades mientras integras los componentes de SwiftUI en tus escenas RealityKit y las entidades impulsan dinámicamente los cambios en tu estado de SwiftUI. Al igual que Sparky y Bolts, esperamos que te inspires para conectar SwiftUI y RealityKit de maneras que no imaginamos. ¡Construyamos el futuro juntos!

    • 1:42 - Sparky in Model3D

      struct ContentView: View {
        var body: some View {
          Model3D(named: "sparky")
        }
      }
    • 1:52 - Sparky in Model3D with a name sign

      struct ContentView: View {
        var body: some View {
          HStack {
            NameSign()
            Model3D(named: "sparky")
          }
        }
      }
    • 3:18 - Display a model asset in a Model3D and present playback controls​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct RobotView: View {
        @State private var asset: Model3DAsset?
        var body: some View {
          if asset == nil {
            ProgressView().task { asset = try? await Model3DAsset(named: "sparky") }
          }
        }
      }
    • 3:34 - Display a model asset in a Model3D and present playback controls​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct RobotView: View {
        @State private var asset: Model3DAsset?
        var body: some View {
          if asset == nil {
            ProgressView().task { asset = try? await Model3DAsset(named: "sparky") }
          } else if let asset {
            VStack {
              Model3D(asset: asset)
              AnimationPicker(asset: asset)
            }
          }
        }
      }
    • 4:03 - Display a model asset in a Model3D and present playback controls​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct RobotView: View {
        @State private var asset: Model3DAsset?
        var body: some View {
          if asset == nil {
            ProgressView().task { asset = try? await Model3DAsset(named: "sparky") }
          } else if let asset {
            VStack {
              Model3D(asset: asset)
              AnimationPicker(asset: asset)
              if let animationController = asset.animationPlaybackController {
                RobotAnimationControls(playbackController: animationController)
              }
            }
          }
        }
      }
    • 4:32 - Pause, resume, stop, and change the move the play head in the animation​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct RobotAnimationControls: View {
        @Bindable var controller: AnimationPlaybackController
      
        var body: some View {
          HStack {
            Button(controller.isPlaying ? "Pause" : "Play") {
              if controller.isPlaying { controller.pause() }
              else { controller.resume() }
            }
      
            Slider(
              value: $controller.time,
              in: 0...controller.duration
            ).id(controller)
          }
        }
      }
    • 5:41 - Load a Model3D using a ConfigurationCatalog​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      struct ConfigCatalogExample: View {
        @State private var configCatalog: Entity.ConfigurationCatalog?
        @State private var configurations = [String: String]()
        @State private var showConfig = false
        var body: some View {
          if let configCatalog {
            Model3D(from: configCatalog, configurations: configurations)
              .popover(isPresented: $showConfig, arrowEdge: .leading) {
                ConfigPicker(
                  name: "outfits",
                  configCatalog: configCatalog,
                  chosenConfig: $configurations["outfits"])
              }
          } else {
            ProgressView()
              .task {
                await loadConfigurationCatalog()
              }
          }
        }
      }
    • 6:51 - Switching from Model3D to RealityView

      struct RobotView: View {
        let url: URL = Bundle.main.url(forResource: "sparky", withExtension: "reality")!
      
        var body: some View {
          HStack {
            NameSign()
            RealityView { content in
              if let sparky = try? await Entity(contentsOf: url) {
                content.add(sparky)
              }
            }
          }
        }
      }
    • 7:25 - Switching from Model3D to RealityView with layout behavior

      struct RobotView: View {
        let url: URL = Bundle.main.url(forResource: "sparky", withExtension: "reality")!
      
        var body: some View {
          HStack {
            NameSign()
            RealityView { content in
              if let sparky = try? await Entity(contentsOf: url) {
                content.add(sparky)
              }
            }
            .realityViewLayoutBehavior(.fixedSize)
          }
        }
      }
    • 8:48 - Switching from Model3D to RealityView with layout behavior and RealityKit animation

      struct RobotView: View {
        let url: URL = Bundle.main.url(forResource: "sparky", withExtension: "reality")!
      
        var body: some View {
          HStack {
            NameSign()
            RealityView { content in
              if let sparky = try? await Entity(contentsOf: url) {
                content.add(sparky)
                sparky.playAnimation(getAnimation())
              }
            }
            .realityViewLayoutBehavior(.fixedSize)
          }
        }
      }
    • 10:34 - Add 2 particle emitters; one to each side of the robot's head

      func setupSparks(robotHead: Entity) {
        let leftSparks = Entity()
        let rightSparks = Entity()
      
        robotHead.addChild(leftSparks)
        robotHead.addChild(rightSparks)
      
        rightSparks.components.set(sparksComponent())
        leftSparks.components.set(sparksComponent())
      
        leftSparks.transform.rotation = simd_quatf(Rotation3D(
          angle: .degrees(180),
          axis: .y))
      
        leftSparks.transform.translation = leftEarOffset()
        rightSparks.transform.translation = rightEarOffset()
      }
      
      // Create and configure the ParticleEmitterComponent
      func sparksComponent() -> ParticleEmitterComponent { ... }
    • 12:30 - Apply the manipulable view modifier

      struct RobotView: View {
        let url: URL
        var body: some View {
          HStack {
            NameSign()
            Model3D(url: url)
              .manipulable()
          }
        }
      }
    • 12:33 - Allow translate, 1- and 2-handed rotation, but not scaling

      struct RobotView: View {
        let url: URL
        var body: some View {
          HStack {
            NameSign()
            Model3D(url: url)
              .manipulable(
                operations: [.translation,
                             .primaryRotation,
                             .secondaryRotation]
             )
          }
        }
      }
    • 12:41 - The model feels heavy with high inertia

      struct RobotView: View {
        let url: URL
        var body: some View {
          HStack {
            NameSign()
            Model3D(url: url)
              .manipulable(inertia: .high)
          }
        }
      }
    • 13:18 - Add a ManipulationComponent to an entity​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

      RealityView { content in
        let sparky = await loadSparky()
        content.add(sparky)
        ManipulationComponent.configureEntity(sparky)
      }
    • 13:52 - Add a ManipulationComponent to an entity​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​ with configuration

      RealityView { content in
        let sparky = await loadSparky()
        content.add(sparky)
        ManipulationComponent.configureEntity(
          sparky,
          hoverEffect: .spotlight(.init(color: .purple)),
          allowedInputTypes: .all,
          collisionShapes: myCollisionShapes()
        )
      }
    • 14:08 - Manipulation interaction events

      public enum ManipulationEvents {
      
        /// When an interaction is about to begin on a ManipulationComponent's entity
        public struct WillBegin: Event { }
        
        /// When an entity's transform was updated during a ManipulationComponent
        public struct DidUpdateTransform: Event { }
      
        /// When an entity was released
        public struct WillRelease: Event { }
      
        /// When the object has reached its destination and will no longer be updated
        public struct WillEnd: Event { }
      
        /// When the object is directly handed off from one hand to another
        public struct DidHandOff: Event { }
      }
    • 14:32 - Replace the standard sounds with custom ones

      RealityView { content in
        let sparky = await loadSparky()
        content.add(sparky)
      
        var manipulation = ManipulationComponent()
        manipulation.audioConfiguration = .none
        sparky.components.set(manipulation)
      
        didHandOff = content.subscribe(to: ManipulationEvents.DidHandOff.self) { event in
          sparky.playAudio(handoffSound)
        }
      }
    • 16:19 - Builder based attachments

      struct RealityViewAttachments: View {
        var body: some View {
          RealityView { content, attachments in
            let bolts = await loadAndSetupBolts()
            if let nameSign = attachments.entity(
              for: "name-sign"
            ) {
              content.add(nameSign)
              place(nameSign, above: bolts)
            }
            content.add(bolts)
          } attachments: {
            Attachment(id: "name-sign") {
              NameSign("Bolts")
            }
          }
          .realityViewLayoutBehavior(.centered)
        }
      }
    • 16:37 - Attachments created with ViewAttachmentComponent

      struct AttachmentComponentAttachments: View {
        var body: some View {
          RealityView { content in
            let bolts = await loadAndSetupBolts()
            let attachment = ViewAttachmentComponent(
                rootView: NameSign("Bolts"))
            let nameSign = Entity(components: attachment)
            place(nameSign, above: bolts)
            content.add(bolts)
            content.add(nameSign)
          }
          .realityViewLayoutBehavior(.centered)
        }
      }
    • 17:04 - Targeted to entity gesture API

      struct AttachmentComponentAttachments: View {
        @State private var bolts = Entity()
        @State private var nameSign = Entity()
      
        var body: some View {
          RealityView { ... }
          .realityViewLayoutBehavior(.centered)
          .gesture(
            TapGesture()
              .targetedToEntity(bolts)
              .onEnded { value in
                nameSign.isEnabled.toggle()
              }
          )
        }
      }
    • 17:10 - Gestures with GestureComponent

      struct AttachmentComponentAttachments: View {
        var body: some View {
          RealityView { content in
            let bolts = await loadAndSetupBolts()
            let attachment = ViewAttachmentComponent(
                rootView: NameSign("Bolts"))
            let nameSign = Entity(components: attachment)
            place(nameSign, above: bolts)
            bolts.components.set(GestureComponent(
              TapGesture().onEnded {
                nameSign.isEnabled.toggle()
              }
            ))
            content.add(bolts)
            content.add(nameSign)
          }
          .realityViewLayoutBehavior(.centered)
        }
      }
    • 0:00 - Introducción
    • Obtén información sobre las mejoras de RealityKit SwiftUI que permiten una integración perfecta de la interfaz de usuario tradicional y el contenido 3D interactivo. Las actualizaciones clave incluyen mejoras en Model3D y RealityView, la presentación de la API Object Manipulation, nuevos tipos de componentes, flujo de datos bidireccional entre SwiftUI y RealityKit, la conversión de espacio de coordenadas más sencilla, y animaciones impulsadas por SwiftUI para componentes RealityKit.

    • 1:24 - Mejoras a Model3D
    • En visionOS 26, Model3D permite la visualización de modelos 3D con animaciones y la carga desde un 'ConfigurationCatalog'. Ahora puedes crear experiencias 3D interactivas con solo unas pocas líneas de código. Utiliza el nuevo tipo 'Model3DAsset' para cargar y controlar animaciones, y 'ConfigurationCatalog' para cambiar entre diferentes representaciones de una entidad, como atuendos o tipos de cuerpo. El ejemplo de un robot llamado Sparky, al que puedes animar, vestir con distintos atuendos y controlar mediante vistas y selectores SwiftUI, preparándolo para interactuar con otros robots en un invernadero virtual, demuestra estas características.

    • 6:13 - Transición de RealityView
    • Para agregar un emisor de partículas a un modelo 3D en tiempo de ejecución, debes cambiar de 'Model3D' a 'RealityView' en RealityKit ya que Model3D no admite la adición de componentes. RealityView se carga con la entidad del modelo, pero esto genera problemas de diseño ya que, de manera predeterminada, ocupa todo el espacio disponible. El modificador 'realityViewLayoutBehavior' se aplica con 'fixedSize' para resolver esto, lo que hace que RealityView ajuste perfectamente los límites iniciales del modelo. Hay tres opciones diferentes de 'realityViewLayoutBehavior': 'flexible', 'centered' y 'fixedSize', cada uno de los cuales afecta el punto de origen de RealityView sin reposicionar las entidades. Utiliza Reality Composer Pro para diseñar emisores de partículas que se puedan configurar en el código. Los emisores de partículas se agregan a las entidades como componentes. RealityKit se basa en el paradigma "Entity Component System". El ejemplo agrega emisores de partículas para crear efectos de chispas, involucrando entidades invisibles como contenedores para las chispas. RealityView es ideal para la creación detallada y el control preciso del comportamiento del contenido 3D, mientras que Model3D es adecuado para mostrar activos 3D autónomos. Puedes realizar una transición suave entre ambos según sea necesario.

    • 11:52 - Manipulación de objetos
    • La nueva API Object Manipulation en visionOS 26 permite interactuar con objetos virtuales en apps que usan SwiftUI y RealityKit. Los usuarios pueden mover, rotar y escalar objetos con sus manos, e incluso pasar objetos entre ellas. Para las vistas SwiftUI, puedes aplicar el modificador 'manipulable', que permite personalizar las operaciones admitidas y la inercia de los objetos. En RealityKit, agrega 'ManipulationComponent' a las entidades a través de la función estática 'configureEntity', la cual también agrega componentes de colisión, de objetivo de entrada y de efecto de desplazamiento. La función tiene varios parámetros para personalizar el comportamiento. Suscribir a 'ManipulationEvents' activados durante interacciones, como inicios, detenciones, actualizaciones, lanzamientos y transferencias, para actualizar el estado de la app y personalizar la experiencia, incluido el reemplazo de sonidos predeterminados con recursos de audio personalizados.

    • 15:35 - Componentes de SwiftUI
    • Los nuevos componentes SwiftUI RealityKit mejoran la interacción y la conexión del usuario dentro de las escenas RealityKit. Estos componentes te permiten integrar sin problemas las vistas SwiftUI en entornos 3D. Las características clave incluyen 'ViewAttachmentComponent', el cual te permite agregar vistas SwiftUI directamente a las entidades; 'GestureComponent', que hace que las entidades respondan al tacto y a los gestos; y 'PresentationComponent', el cual permite la presentación de vistas SwiftUI, como ventanas emergentes dentro de la escena. El parámetro de configuración en 'PresentationComponent' es el tipo de presentación a mostrar. Estas mejoras simplifican el proceso de desarrollo y te permiten crear experiencias más dinámicas y atractivas. En el ejemplo, un robot llamado Bolts puede tener un cartel con su nombre que se activa y desactiva con un toque y las personas pueden elegir el atuendo de Bolts desde un menú emergente, todo dentro del entorno inmersivo de RealityKit.

    • 19:08 - Flujo de información
    • En visionOS 26, las entidades ahora son observables, lo que permite que entidades como el robot Bolts notifiquen a otro código cuando sus propiedades cambian. Esto es particularmente útil para rastrear los movimientos de Bolts mientras riega las plantas en el invernadero. El ejemplo utiliza SwiftUI para implementar un minimapa que muestra la posición de Bolts en tiempo real. Al acceder a la propiedad de posición observable de Bolts, SwiftUI actualiza automáticamente el minimapa cada vez que Bolts se mueve. Este flujo de datos bidireccional entre SwiftUI y RealityKit, habilitado por entidades observables, es una nueva herramienta importante. Esta nueva funcionalidad observable también introduce la posibilidad de bucles infinitos si no se gestiona con cuidado. Para evitar estos bucles, cuida dónde modificas el estado observado. No realices cambios en el estado observado dentro del cierre de 'actualización' de 'RealityView', porque esto puede desencadenar un ciclo de actualización recursivo. Mejor, realiza modificaciones en lugares específicos, como sistemas personalizados, cierres de gestos o el cierre 'make' que están fuera del alcance de la evaluación del cuerpo de la vista de SwiftUI. Al dividir vistas más grandes en vistas más pequeñas y autónomas y cuidar dónde se modifica el estado, puedes garantizar un flujo de datos eficiente y sin problemas entre SwiftUI y RealityKit. Con esto evitas bucles infinitos y mejoras el rendimiento general de la app.

    • 24:56 - Conversión de coordenadas unificada
    • La nueva API Unified Coordinate Conversion cierra la brecha entre RealityKit y SwiftUI. Al implementar el protocolo 'CoordinateSpace3D', puedes convertir valores sin problemas entre las dos estructuras. Esto permite calcular la distancia absoluta entre Bolts, una entidad en RealityKit, y Sparky, un Model3D en SwiftUI, a medida que se acercan y generan chispas dinámicamente en función de su proximidad.

    • 27:01 - Animación
    • En visionOS 26, ahora puedes aprovechar las API de animación de SwiftUI para animar implícitamente los cambios en los componentes de RealityKit. Esto permite movimientos suaves y dinámicos de las entidades simplemente al configurar los componentes compatibles dentro de un bloque de animación. Hay dos métodos para lograr esto: utilizar 'content.animate()' dentro de una RealityView o llamando a 'Entity.animate()' directamente. Esta integración permite comportamientos de liberación personalizados al manipular entidades, lo que hace que las interacciones con objetos 3D sean más atractivas y divertidas. Varios componentes de RealityKit, incluidos Transform, Audio, Model y Light, admiten estas animaciones implícitas.

    • 29:41 - Próximos pasos
    • Utiliza esta nueva conexión entre SwiftUI y RealityKit para crear aplicaciones espaciales innovadoras al integrar componentes de SwiftUI con escenas de RealityKit, lo cual permite interacciones dinámicas entre los mundos virtuales y físicos e inspira nuevas posibilidades en el desarrollo de apps.

Developer Footer

  • Videos
  • WWDC25
  • Mejor juntos: SwiftUI y RealityKit
  • 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