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

Vídeos

Abrir menu Fechar menu
  • Coleções
  • Tópicos
  • Todos os vídeos
  • Sobre

Mais vídeos

  • Sobre
  • Resumo
  • Transcrição
  • Código
  • Melhor juntos: SwiftUI e RealityKit

    Descubra como integrar perfeitamente o SwiftUI e o RealityKit no visionOS 26. Vamos explorar as melhorias do Model3D, incluindo suporte a animações e compatibilidade com o ConfigurationCatalog, além de demonstrar transições suaves no RealityView. Você aprenderá a usar as animações do SwiftUI para controlar mudanças nos componentes do RealityKit, implementar manipulação interativa, usar os novos componentes do SwiftUI para criar interações mais avançadas e monitorar as alterações do RealityKit com base no seu código do SwiftUI. Também abordaremos o uso da conversão unificada de coordenadas para realizar transformações entre frameworks.

    Capítulos

    • 0:00 - Introdução
    • 1:24 - Aprimoramentos do Model3D
    • 6:13 - Transição para o RealityView
    • 11:52 - Manipulação de objetos
    • 15:35 - Componentes do SwiftUI
    • 19:08 - Fluxo de informações
    • 24:56 - API Unified Coordinate Conversion
    • 27:01 - Animação
    • 29:41 - Próximas etapas

    Recursos

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

    Vídeos 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 neste vídeo...

    Olá! Meu nome é Amanda. Sou engineer do RealityKit. Meu nome é Maks. Sou engineer da SwiftUI. Hoje mostraremos ótimas melhorias na SwiftUI e no RealityKit que os tornam ainda melhores juntos. Veja esta cena adorável. Temos um robô da SwiftUI flutuando e um robô do RealityKit fixo, ambos buscando conexão. Quando se aproximam, vemos faíscas. Mas como podem se aproximar o suficiente para interagir? Maks e eu vamos ensinar como combinar conteúdo de interface tradicional e 3D interativo. Primeiro, vou mostrar novidades do Model3D. Mostrarei como migrar do Model3D para o RealityView e quando escolher cada um. E apresentarei a nova API Object Manipulation.

    O RealityKit ganhou novos tipos de componentes, integrando mais recursos da SwiftUI. As informações podem fluir nos dois sentidos entre a SwiftUI e o RealityKit. Vamos explicar. A conversão entre espaços de coordenadas ficou mais fácil. Controle componentes do RealityKit usando animações da SwiftUI. Vamos conectar tudo!

    Mostre modelos 3D nos seus apps com apenas uma linha de código no Model3D. No visionOS 26, fizemos duas melhorias no Model3D: animações e carregamento via ConfigurationCatalog. Como o Model3D é uma view da SwiftUI, ele participa do sistema de layout da SwiftUI. Vou usar esse sistema de layout para criar uma placa com o nome do robô.

    Agora, a placa mostra que o nome do robô é Sparky.

    O Sparky também sabe dançar muito bem! O artista já incluiu essa animação no arquivo do modelo do robô. Uma novidade do visionOS 26 é o tipo Model3DAsset. Carregue e controle animações no conteúdo 3D criando um Model3D com um Model3DAsset. O modelo carrega as animações do ativo e permite escolher qual reproduzir. “Modelo” tem vários significados. Nesta sessão, há convergência entre um framework de interface com um framework de jogos 3D. Em frameworks de interface, modelo é à estrutura de dados que representa as informações usadas pelo app. O modelo armazena os dados e as regras de negócios para a view exibir essas informações. Em frameworks 3D, como o RealityKit, um modelo é um objeto 3D que pode ser colocado em uma cena. Você acessa esse objeto pelo ModelComponent, formado por um mesh resource que define o formato e os materiais que definem a aparência. Às vezes, isso acontece. Dois mundos se encontram, cada um com seus termos, e por vezes acabam se sobrepondo. Agora, vamos voltar ao Sparky e sua animação.

    Coloquei o Model3D acima de um seletor, um botão de reprodução e um controle de tempo. Na RobotView, exibo o robô animado, e abaixo coloco um seletor para escolher a animação, junto com os controles de reprodução. Antes, inicializo um Model3DAsset com o nome da cena que quero carregar do pacote. Depois de carregar o recurso, passo para o inicializador do Model3D. Abaixo disso, no VStack, mostro um seletor personalizado que lista todas as animações disponíveis nesse ativo de modelo. Quando um item é escolhido na lista, o seletor define a selectedAnimation do ativo para o novo valor. O Model3DAsset cria um AnimationPlaybackController para controlar a reprodução da animação escolhida. O recurso fornece um animationPlaybackController. Esse objeto pausa, retoma e faz buscas na animação.

    Eu passo esse animationController para a minha view RobotAnimationControls, que veremos a seguir. No visionOS 26, a classe atual do RealityKit, AnimationPlaybackController, agora é observável. Na view da SwiftUI, confiro o progresso da animação na propriedade “time”.

    Há uma propriedade @Bindable chamada “controller”, ou seja, eu uso o AnimationPlaybackController como modelo de dados. Quando o valor de isPlaying do controller muda, a SwiftUI reavalia a view RobotAnimationControls. Há um controle que exibe o tempo atual da animação em relação à duração total. Arraste esse controle deslizante para navegar pela animação. Veja o Sparky fazendo a animação de comemoração. Posso adiantar ou voltar a animação com o controle deslizante. Vamos, Sparky, é seu aniversário! Agora que o Sparky mostrou o que sabe fazer, ele quer se arrumar para conhecer o outro robô. Eu posso ajudar com as melhorias no tipo ConfigurationCatalog do RealityKit. Esse tipo armazena diferentes versões de uma entidade, como diferentes formas, valores de componentes ou propriedades de materiais. No visionOS 26, dá para inicializar um Model3D com um ConfigurationCatalog e mudar entre as representações.

    Para que o Sparky troque de roupa, o artista incluiu um reality file com vários tipos de corpo. Carrego esse arquivo como um ConfigurationCatalog no pacote principal do app. Aí, crio o Model3D usando essa configuração. Esse popover mostra as opções de configuração. Quando escolho uma, a aparência do Sparky muda. Passos de dança? Ok. Roupas? Ok. O Sparky está pronto para conhecer seu novo amigo na estufa do RealityKit. Pode apostar: vai sair faísca! Para ver essas faíscas, vou usar um emissor de partículas. Mas não é algo que eu consiga fazer no tempo de execução com o Model3D. Um emissor de partículas é um componente que adiciono a uma entidade do RealityKit. Explico isso melhor a seguir. Um detalhe importante é que o Model3D não permite adicionar componentes. Por isso, para adicionar um emissor de partículas, vou trocar para o RealityView. Quero mostrar como substituir o Model3D pelo RealityView sem alterar o layout. Primeiro, mudo a view do Model3D para o RealityView. Carrego o modelo do botânico do pacote do app no make closure do RealityView, criando uma entidade. Adiciono a entidade ao RealityView, e o Sparky aparece na tela.

    Mas... Agora, a placa de nome ficou muito longe. Isso não acontecia no Model3D. Isso acontece porque o RealityView ocupa todo o espaço disponibilizado pelo layout da SwiftUI. Já o Model3D usa o tamanho intrínseco do arquivo do modelo subjacente. Vou corrigir isso. Aplico o modificador .realityViewLayoutBehavior com .fixedSize para ajustar o RealityView aos limites do modelo. Bem melhor. O RealityView usa os limites visuais das entidades para identificar o tamanho. Esse cálculo é feito uma vez, logo após o make closure ser executado. As outras opções para realityViewLayoutBehavior são .flexible e .centered. Nestes três RealityViews, a base do Sparky fica na origem da cena, marcada por um gizmo, a cruz colorida que mostra os eixos e a origem. À esquerda, com .flexible, o RealityView ignora o modificador. A origem permanece centralizada na visualização. A opção ".centered" centraliza o conteúdo no RealityView. A opção .fixedSize ajusta o RealityView ao conteúdo, como ocorre com o Model3D.

    Essas opções não reposicionam ou dimensionam entidades, apenas o ponto de origem do RealityView. Ajustei o tamanho do Sparky no RealityView. Agora vou animar o Sparky novamente. Vou migrar da API de animação do Model3D para a API de animação do RealityKi na entidade. Para saber como trabalhar com animação no RealityKit, veja a sessão “Compor conteúdo 3D interativo no Reality Composer Pro”. Troquei o Model3D pelo RealityView para incluir ParticleEmitterComponent ao Sparky para aparecem faíscas quando os robôs se aproximarem. Os emissores de partículas permitem criar efeitos com centenas de partículas, como fogos, chuva e brilhos. O RealityKit fornece valores predefinidos para eles, e você pode ajustar tudo para obter o efeito desejado. Use o Reality Composer Pro para criar emissores ou configure-os no código. Adicione o emissor de partículas como um componente na entidade. Esses componentes são essenciais no RealityKit e são baseados no paradigma Sistema de componentes de entidade. Cada objeto é uma entidade, e os components definem sua características e comportamentos. Um componente armazena dados sobre uma entidade. Um sistema processa entidades com certos componentes, aplicando lógicas a esses dados. Existem sistemas integrados para animar as partículas, aplicar a física, renderizar e muito mais. Você pode criar sistemas personalizados no RealityKit para a lógica do seu jogo ou app. Confira “Conhecer o RealityKit 2” para saber detalhes sobre o Sistema de componentes de entidade no RealityKit. Vou adicionar um emissor de partículas para cada lado da cabeça do Sparky. Primeiro, crio duas entidades invisíveis que servem como contêineres do efeito. Projetei o emissor para apontar à direita. Adiciono este emissor à entidade invisível do lado direito do Sparky.

    No lado oposto, rotaciono a entidade 180° no eixo Y para apontar à esquerda.

    Combinando tudo no RealityView, temos o Sparky e sua animação, sua placa de nome no lugar certo e muitas faíscas.

    O RealityKit é excelente para criação detalhada. Para jogos, experiências lúdicas ou controle preciso de comportamentos 3D, escolha o RealityView. Use o Model3D para exibir um recurso 3D independente. Pense no Model3D como o Image da SwiftUI, mas para 3D.

    Os novos catálogos de animação e configuração ampliam o que o Model3D pode fazer. Se precisar de acesso direto às entidades e sistemas, faça a transição do Model3D para o RealityView usando o realityViewLayoutBehavior. Agora vou detalhar a nova API Object Manipulation no visionOS 26, que permite pegar objetos virtuais no app. A manipulação funciona tanto no SwiftUI quanto no RealityKit. Com ela, mova o objeto com uma mão, gire com uma ou ambas e dimensione com movimento de abrir e fechar as mãos. Também é possível passar o objeto de uma mão para outra.

    Há dois modos de ativar isso, dependendo se o objeto é uma entidade do RealityKit ou uma view da SwiftUI. Na SwiftUI, adicione o novo modificador manipulable. Para desativar o redimensionamento, mas permitir mover e girar, defino as Operations compatíveis.

    Para dar sensação de peso ao robô, defino inertia como high.

    O modificador .manipulable funciona quando o Sparky está em uma view do Model3D. Ele é aplicado ao Model3D inteiro ou a qualquer view à qual estiver anexado. No RealityView, prefiro ativar a manipulação só para o robô, não para todo o RealityView. No visionOS 26, o ManipulationComponent é um novo tipo para permitir a manipulação de objetos em uma entidade. A função estática configureEntity adiciona o ManipulationComponent à entidade. Ela também adiciona o CollisionComponent para que o sistema reconheça toques. E adiciona InputTargetComponent, indicando que a entidade responde a gestos. Por fim, um HoverEffectComponent aplica o efeito visual quando alguém olha para ele ou passa o mouse. Essa é a única linha necessária para ativar a manipulação em uma entidade. Para personalizar ainda mais, há vários parâmetros opcionais. Aqui, uso um efeito de holofote roxo. Permito todos os tipos de entrada: toque direto, olhar indireto e pinça. Defino formas de colisão que contornam o robô. Para responder à interação do usuário com um objeto no app, o sistema de manipulação dispara eventos em momentos importantes: quando a interação começa e termina, atualiza enquanto a entidade é movida, girada e redimensionada, quando é solta e quando é transferida de uma mão para outra. Assine esses eventos para atualizar o estado. Normalmente, sons padrão são reproduzidos quando a interação começa, ocorre uma transferência de mão ou o objeto é solto. Para aplicar sons personalizados, primeiro defini audioConfiguration como none. Isso desativa os sons padrão. Depois, assino o evento ManipulationEvent DidHandOff, disparado quando alguém passa o robô de uma mão para a outra. Nesse closure, toco o áudio personalizado. Bem, Maks. A jornada do Sparky tem sido emocionante: movendo-se no Model3D, buscando um lar em um RealityView, mostrando sua personalidade com faíscas e permitindo interação direta. Foi um grande avanço em direção à estufa do RealityKit. Sem dúvida. Mas para que o Sparky se conecte com o outro robô, os objetos no espaço virtual precisam de novos recursos. Eles devem reagir a gestos, mostrar informações sobre si e acionar ações de modo nativo na SwiftUI.

    A jornada do Sparky rumo à estufa do RealityKit se resume a criar conexões. Conexões profundas exigem interações avançadas. É exatamente isso que os novos componentes da SwiftUI do RealityKit oferecem. Os novos componentes do visionOS 26 trazem ao RealityKit recursos eficientes e conhecidos do SwiftUI. O RealityKit apresenta três componentes principais: Primeiro, o ViewAttachmentComponent permite adicionar views da SwiftUI às suas entidades. Depois, o GestureComponent torna as entidades responsivas a toque e gestos. E o PresentationComponent, que mostra views da SwiftUI, como popovers, de dentro da sua cena do RealityKit.

    No visionOS 1, você declarava attachments como parte do inicializador do RealityView. Após avaliar seu attachment view builder, o sistema chamava o update closure, entregando os resultados como entidades. Assim, você podia adicionar essas entidades à cena e posicioná-las no espaço 3D. No visionOS 26, isso foi simplificado. Agora, você cria attachments usando um componente do RealityKit de qualquer lugar do app. Basta criar um ViewAttachmentComponent informando qualquer view da SwiftUI. Depois, adicione-o aos componentes de uma entidade.

    Assim, movi o NameSign da SwiftUI para o RealityKit facilmente. Vamos explorar gestos agora! Você já pode anexar gestos ao seu RealityView usando modificadores de gesto targetedToEntity. A novidade do visionOS 26 é o GestureComponent. Assim como com o ViewAttachmentComponent, adicione o GestureComponent às entidades, passando gestos do SwiftUI tradicionais. Os valores dos gestos, por padrão, são informados no espaço da entidade. Muito prático! Uso o GestureComponent com um toque para alternar a placa de nome.

    Veja. O nome deste robô é... Bolts!

    Dica: Em qualquer entidade-alvo de gestos, adicione o InputTargetComponent e o CollisionComponent. Essa dica vale para o GestureComponent e para a API de gestos.

    GestureComponent e ViewAttachmentComponent facilitam criar uma placa de nome para o Bolts. Mas o Bolts está se preparando para uma visita especial: o Sparky! O Bolts quer estar impecável para o encontro deles na estufa. Hora de trocar de roupa. Vou substituir o nome por uma interface para escolher a roupa. Uma decisão muito importante.

    Vou mostrar isso em um popover na interface, usando o PresentationComponent pelo RealityKit.

    Primeiro, substituo o ViewAttachmentComponent pelo PresentationComponent. Esse componente recebe uma vinculação booleana para controlar quando o popover é exibido e para notificar quando ele for fechado. O parâmetro configuration define o tipo de apresentação desejada. Estou especificando o popover. Dentro do popover, apresento uma view com opções de um catálogo de configurações para vestir o Bolts. Assim, posso ajudar o Bolts a escolher sua melhor cor para receber a visita do Sparky.

    Maks, você acha que o Bolts gosta do verão? Ou prefere o outono?

    É uma piada sobre moda.

    O Bolts está vestido para arrasar. Mas primeiro, ele precisa trabalhar. O Bolts rega as plantas ne cenário. Vou criar um minimapa, como os dos jogos, para rastrear a posição do Bolts na estufa. Para isso, preciso observar o componente de transformação do robô. No visionOS 26, as entidades são observáveis. Elas podem notificar outros códigos quando suas propriedades mudam. Para ser notificado, basta acessar a propriedade observable.

    Nessa propriedade, você pode acompanhar mudanças na posição, tamanho e rotação, na coleções secundárias e até nos componentes, inclusive os personalizados. Observe essas propriedades utilizando um bloco withObservationTracking. Ou use o sistema de observação integrado da própria SwiftUI. Usarei a SwiftUI para implementar meu minimapa. Saiba mais sobre observação na sessão “Conhecer a observação ba SwiftUI”. Nesta view, mostro a posição da minha entidade em MiniMap. Acesso esse valor observável direto na entidade. Assim, informo à SwiftUI que minha view depende desse valor.

    Conforme o Bolts se move pela estufa, regando as plantas, sua posição muda. Cada vez que isso acontece, a SwiftUI chama o body da minha view, movendo o símbolo no minimapa. Para entender o fluxo de dados da SwiftUI, confira “Fundamentos sobre dados na SwiftUI”. Nossos amigos robôs estão tomando forma. É isso que queremos! Gostei da sua explicação sobre as diferenças de significado do termo “modelo”. Às vezes, você precisa passar dados do seu modelo de dados para o modelo 3D, e vice-versa. No visionOS 26, entidades observáveis são uma nova ferramenta para isso. Desde o início, já era possível passar informações da SwiftUI para o RealityKit no fechamento update do RealityView. Agora, a propriedade observable da entidade permite enviar informações no sentido contrário. Entidades do RealityKit podem atuar como objetos do modelo para atualizar views da SwiftUI. Então, agora o fluxo de dados é bidirecional: do SwiftUI para a RealityKit e do RealityKit para a SwiftUI. Mas... será que isso pode criar um loop infinito? Sim. Vejamos como evitar loops infinitos entre a SwiftUI e o RealityKit. Ao ler uma propriedade observable dentro do body de uma view, a view passa a depender dessa propriedade. Se o valor da propriedade mudar, a SwiftUI atualiza a view e executa o body novamente. O RealityView tem um comportamento especial. Pense no fechamento update como uma extensão do corpo da view que o contém.

    A SwiftUI chama o fechamento sempre que um estado da view muda, não só quando o estado explicitamente observado naquele fechamento muda. No fechamento update do meu RealityView, vou alterar essa posição. Isso gravará no valor da posição, o que faz com que a SwiftUI atualize a view e reexecute o body, criando um loop infinito.

    Para evitar o loop infinito, não modifique um estado observado dentro do seu fechamento update.

    Você pode modificar entidades que não estão sendo observadas. Isso não cria o loop, porque as mudanças nelas não vão disparar uma reavaliação do body da view pela SwiftUI. Se precisar modificar uma propriedade observada, verifique antes o valor atual e evite regravar o mesmo valor. Isso quebra o ciclo e impede o loop infinito. Note que o make closure do RealityView é especial. Acessar uma propriedade observável dentro do make closure não cria dependência. Ela não é incluída no escopo de observação da view. O make closure não é executado novamente nas alterações. Ele só é executado quando a view que o contém aparece pela primeira vez. Você também pode atualizar propriedades de uma entidade observada dentro do seu sistema personalizado. A função de update de um sistema não está no escopo de avaliação do body da SwiftUI, então é um ótimo lugar para modificar valores observados de entidades.

    Os fechamentos de gestos também não estão no escopo a avaliação do body da SwiftUI. Eles são chamados em resposta à interação do usuário. Você também pode modificar os valores das entidades observadas aqui. Em resumo, é seguro modificar as entidades observadas em lugares específicos.

    Se você encontrar um loop infinito no app, aqui vai uma dica para corrigir: Divida as views maiores em views menores e independentes, cada uma apenas com o estado necessário. Assim, uma mudança em uma entidade não relacionada não fará com que a view menor seja reavaliada. Isso também é ótimo para o desempenho! Maks, pode ser que você nem precise usar mais o fechamento update. Já que sua entidade pode ser o estado da view, você pode modificá-la nos lugares em que já muda o estado e ignorar o fechamento update. É verdade. Evitar loops infinitos é algo que sempre preciso reaprender. Mas sem o update closure, é menos provável que eu crie um. Acho que é hora de reunir o Bolts e o Sparky. O Bolts finalmente acabou o trabalho e pode encontrar o Sparky. Enquanto levo o Sparky até o outro robô e os dois se aproximam, quero que faíscas comecem a surgir conforme eles se aproximam. Vou usar a nova API Unified Coordinate Conversion para isso. O Sparky está em uma view da SwiftUI do Model3D, e o Bolts é uma entidade na estufa do RealityKit. Preciso da distância absoluta entre eles, mesmo estando em espaços de coordenadas diferentes. Para resolver isso, o framework Spatial define agora um protocolo CoordinateSpace3D, que representa um espaço de coordenadas abstrato. Você pode facilmente converter valores entre tipos que adotam o CoordinateSpace3D, até entre diferentes frameworks. Os tipos Entity e Scene do RealityKit agora adotam o CoordinateSpace3D. No lado da SwiftUI, o GeometryProxy3D possui agora a função .coordinateSpace3D(), que retornará seu espaço de coordenadas. Além disso, vários tipos de gesto podem fornecer valores relativos a qualquer CoordinateSpace3D informado. O protocolo CoordinateSpace3D funciona convertendo um valor do espaço do Sparky para um espaço compartilhado entre o RealityKit e a SwiftUI. Depois disso, ele converte do espaço compartilhado para o do Bolts, considerando detalhes como conversão de pontos para metros e direção dos eixos. Na view do Model3D do Sparky, sempre que a geometria da view muda, o sistema chama a função onGeometryChange3D. Ela recebe um GeometryProxy3D, que uso para obter o espaço de coordenadas. Assim posso converter a posição da view para um ponto no espaço da entidade e saber a distância real entre os dois robôs. Agora, quando Amanda aproxima o Bolts e o Sparky, as faíscas aumentam. Quando se afastá-os, as faíscas diminuem.

    A seguir, vou ensinar esses robôs a se moverem juntos e coordenarem suas ações. Vou usar a animação feita pela SwiftUI para componentes do RealityKit. A SwiftUI já oferece ótimas APIs para animar mudanças em propriedades das views. Neste exemplo, animo a view do Model3D onde o Sparky está: Mando ele para a esquerda ao alternar, e ele quica de volta para a posição original ao alternar novamente. Adiciono uma animação à vinculação isOffset, pedindo uma animação de quicar. No visionOS 26, é possível usar a animação da SwiftUI implicitamente nas mudanças de componentes do RealityKit. Basta definir um componente compatível na entidade em um bloco de animação RealityKit, e o framework cuida do restante. Há duas formas de associar animação a uma mudança de estado. Dentro de um RealityView, use o content.animate() para definir novos valores dos componentes dentro do bloco de animação. O RealityKit usará a animação associada à transação da SwiftUI que acionou o fechamento update, neste caso, a ação de quicar.

    A outra forma é chamar Entity.animate(), passando uma animação da SwiftUI e um fechamento que modifica os componentes. Assim, sempre que a propriedade isOffset mudar, mando o Sparky para a esquerda ou a direita usando a posição da entidade. Ao definir a posição dentro do bloco animate, inicio uma animação implícita de transformação, mudando a posição da entidade suavemente. A força da animação implícita se destaca quando combino com a API Object Manipulation apresentada pela Amanda. Posso usar uma animação da SwiftUI para personalizar o comportamento de soltar ao Bolts. Primeiro, desativo o comportamento padrão de soltar ao definir para .stay. Depois, assino o evento WillRelease na interação de manipulação. Quando o objeto estiver para ser solto, mudo o transform do Sparky para identity, redefinindo o tamanho, a translação e a rotação. Como modifiquei o transform do Sparky no bloco animate, ele quica de volta para o lugar. Agora, a animação de retorno do Sparky ficou bem mais divertida! Esses componentes integrados do RealityKit têm animações implícitas, incluindo o transform, o componente Audio e os componentes Model e Light, que têm propriedades de cor. O Sparky e o Bolts tiveram uma jornada e tanto. É ótimo ver o poder da SwiftUI e do RealityKit juntos. Com essa integração, você pode criar apps espaciais realmente excepcionais, impulsionando a conexão entre o virtual e o físico. Imagine as possibilidades ao integrar os componentes da SwiftUI nas cenas do RealityKit e permitir que entidades mudem dinamicamente o estado da SwiftUI. Assim como o Sparky e o Bolts, esperamos inspirar você a conectar a SwiftUI e o RealityKit com ainda mais criatividade. Vamos construir o 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 - Introdução
    • Saiba mais sobre os aprimoramentos do SwiftUI do RealityKit que permitem a integração perfeita da interface do usuário tradicional e do conteúdo 3D interativo. As principais atualizações incluem aprimoramentos no Model3D e no RealityView, a introdução da API Object Manipulation, novos tipos de componente, fluxo de dados bidirecional entre o SwiftUI e o RealityKit, conversão de espaço de coordenadas facilitada e animações orientadas por SwiftUI para componentes do RealityKit.

    • 1:24 - Aprimoramentos do Model3D
    • No visionOS 26, o Model3D permite exibir modelos 3D com animações e carregar a partir de um "ConfigurationCatalog". Agora você pode criar experiências 3D interativas com apenas algumas linhas de código. Use o novo tipo "Model3DAsset" para carregar e controlar animações, e o "ConfigurationCatalog" permite alternar entre diferentes representações de uma entidade, como roupas ou tipos de corpo. Para demonstrar esses recursos, temos o exemplo de um robô chamado Sparky, que você pode animar, vestir com diferentes roupas e controlar usando visualizações e seletores SwiftUI, preparando-o para interagir com outros robôs em uma estufa virtual.

    • 6:13 - Transição para o RealityView
    • Para adicionar um emissor de partículas a um modelo 3D em tempo de execução, você deve alternar do uso de "Model3D" para "RealityView" no RealityKit porque o Model3D não suporta a adição de componentes. O RealityView é carregado com a entidade modelo, mas isso causa problemas no layout, pois o padrão ocupa todo o espaço disponível. O modificador "realityViewLayoutBehavior" é aplicado com "fixedSize" para resolver esse problema, ajustando o RealityView ao modelo. Há três opções de comportamento diferentes de "realityViewLayoutBehavior". "flexible", "centered" e "fixedSize", cada um afetando o ponto de origem do RealityView sem reposicionar as entidades. Use o Reality Composer Pro para projetar emissores de partículas que podem ser configurados no código. Emissores de partículas são adicionados a entidades como componentes. O RealityKit tem como base o paradigma "Entity Component System". O exemplo inclui emissores de partículas para criar efeitos de faísca, envolvendo entidades invisíveis como contêineres para as faíscas. O RealityView é ótimo para criação detalhada e controle refinado sobre o comportamento do conteúdo 3D, enquanto o Model3D é adequado para exibir ativos 3D independentes. Você pode fazer a transição suave entre os dois conforme necessário.

    • 11:52 - Manipulação de objetos
    • A nova API Object Manipulation no visionOS 26 permite interagir com objetos virtuais em apps que usam SwiftUI e RealityKit. As pessoas podem mover, girar e escalar objetos com as mãos, e até mesmo passar objetos entre as mãos. Para visualizações do SwiftUI, você pode aplicar o modificador 'manipulable', permitindo a personalização das operações suportadas e a inércia do objeto. No RealityKit, adicione o "ManipulationComponent" às entidades por meio da função estática "configureEntity", que também adiciona componentes de colisão, destino de entrada e efeito de foco. A função tem vários parâmetros para personalização de comportamento. Assine "ManipulationEvents" acionados durante interações, como inícios, paradas, atualizações, lançamentos e transferências, para atualizar o estado do app e personalizar a experiência, incluindo a substituição de sons padrão por recursos de áudio personalizados.

    • 15:35 - Componentes do SwiftUI
    • Os novos componentes do SwiftUI RealityKit melhoram a interação e a conexão do usuário dentro das cenas do RealityKit. Esses componentes permitem integrar perfeitamente visualizações do SwiftUI em ambientes 3D. Os principais recursos incluem o "ViewAttachmentComponent", que permite adicionar visualizações do SwiftUI diretamente a entidades; o "GestureComponent", tornando as entidades responsivas ao toque e movimentos; e o "PresentationComponent", que permite apresentar visualizações do SwiftUI, como popovers, dentro da cena. O parâmetro de configuração no "PresentationComponent" define o tipo de apresentação para exibição. Essas melhorias simplificam o processo de desenvolvimento, permitindo criar experiências mais dinâmicas e envolventes. No exemplo, um robô chamado Bolts pode ter um sinal de nome que liga e desliga com um movimento de toque, e as pessoas podem escolher a roupa dele em um menu popover, tudo dentro do ambiente imersivo do RealityKit.

    • 19:08 - Fluxo de informações
    • No visionOS 26, as entidades agora são observáveis, o que permite que entidades, como o robô Bolts, notifiquem outro código quando suas propriedades mudarem. Isso é particularmente útil para rastrear os movimentos do Bolts enquanto rega as plantas na estufa. O exemplo usa o SwiftUI para implementar um minimapa que exibe a posição do Bolts em tempo real. Acessando a propriedade de posição observável do Bolts, o SwiftUI atualiza automaticamente o minimapa sempre que Bolts se move. Esse fluxo de dados bidirecional entre o SwiftUI e o RealityKit, habilitado por entidades observáveis, é uma nova ferramenta significativa. Essa nova funcionalidade observável também introduz o potencial de loops infinitos, caso não seja gerenciada com cuidado. Para evitar esses loops, atente-se a onde você modifica o estado observado. Não faça alterações no estado observado dentro do fechamento "update" do "RealityView", porque isso pode desencadear um ciclo de atualização recursivo. Em vez disso, faça modificações em locais específicos, como sistemas personalizados, fechamentos por movimento ou o fechamento "make", que estão fora do escopo da avaliação do corpo de visualização da SwiftUI. Dividindo visualizações maiores em visualizações menores e independentes e tendo cautela quanto a onde o estado é modificado, você pode garantir um fluxo de dados suave e eficiente entre o SwiftUI e o RealityKit, evitando loops infinitos e melhorando o desempenho geral do app.

    • 24:56 - API Unified Coordinate Conversion
    • A nova API de conversão de coordenadas unificadas preenche a lacuna entre o RealityKit e o SwiftUI. Implementando o protocolo "CoordinateSpace3D", você pode converter valores perfeitamente entre os dois frameworks. Isso permite calcular a distância absoluta entre Bolts, uma entidade no RealityKit, e Sparky, um Model3D em SwiftUI, à medida que se aproximam, gerando faíscas dinamicamente com base em sua proximidade.

    • 27:01 - Animação
    • No visionOS 26, é possível aproveitar as APIs de animação do SwiftUI para animar implicitamente as mudanças de componentes do RealityKit. Isso permite movimentos suaves e dinâmicos de entidades simplesmente definindo componentes compatíveis dentro de um bloco de animação. Existem dois métodos para conseguir isso: usando "content.animate()" dentro de um RealityView ou chamando "Entity.animate()" diretamente. Essa integração permite comportamentos personalizados de liberação ao manipular entidades, tornando as interações com objetos 3D mais envolventes e divertidas. Vários componentes do RealityKit, incluindo Transformar, Áudio, Modelo e Luz, oferecem suporte a essas animações implícitas.

    • 29:41 - Próximas etapas
    • Use essa nova conexão entre SwiftUI e RealityKit para criar apps espaciais inovadores, integrando componentes SwiftUI com cenas do RealityKit, permitindo interações dinâmicas entre os mundos virtual e físico e inspirando novas possibilidades no desenvolvimento de apps.

Developer Footer

  • Vídeos
  • WWDC25
  • Melhor juntos: SwiftUI e 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