Performance e interoperabilidade do SwiftUI no iOS 27
Um LazyVStack não sabe qual é a sua altura. Ele estima a própria altura a partir do tamanho médio das views que já posicionou e do número de views que espera que ainda restem, e então corrige essa estimativa ao vivo conforme você rola.1 Três sessões da WWDC26 da equipe de UI Frameworks pegam esse único fato (e seus análogos em gráficos e interoperabilidade) e o transformam em um modelo funcional de como o SwiftUI se comporta sob carga no iOS 27: como a rolagem permanece suave, como os efeitos da GPU se compõem e como o SwiftUI se encaixa em um app AppKit ou UIKit que você já lançou.
As três sessões se leem como um único argumento feito de três maneiras. As lazy stacks têm bom desempenho quando você para de lutar contra a estimativa delas; os efeitos de shader se compõem quando você trata cada modifier como um estágio em um pipeline; e a interoperabilidade funciona quando você deixa o @Observable e os protocolos representable carregarem a costura. Nenhuma das três é o anúncio de um recurso. Cada uma é um mecanismo explicado bem o suficiente para que você possa prever o framework em vez de adivinhar como ele se comporta.
TL;DR / Principais conclusões
- Um
LazyVStacksó avalia as views que preenchem o retângulo visível; as alturas fora da tela e o offset do conteúdo são estimados, então as leituras absolutas do offset do conteúdo são instáveis e você deve preferir APIs de visibilidade relativa como.onScrollTargetVisibilityChange.12 - O prefetching divide o trabalho de exibir uma view ao longo de vários frames antes que ela apareça; configure as views no inicializador delas (não no
onAppear) para que o trabalho de prefetch não seja descartado.1 - Evite um número dinâmico de subviews em uma folha de
ForEach: filtrar com uma condicional nobodymantém as views vivas por índice, então filtre no nível dos dados (umPredicateem umaQuery).1 - O SwiftUI expõe três pontos de entrada de shader (
colorEffect,distortionEffect,layerEffect), crescentes em poder; apenas olayerEffectconsegue amostrar pixels vizinhos, o que é exatamente do que o blur e o domain-warp precisam.3456 - Os shaders não têm estado, então a animação vem de alimentar um timestamp de
TimelineViewcomo parâmetro; nada é carregado de um frame para o outro.37 - O AppKit e o UIKit ganham redesenho automático a partir do
@Observable(sem maisneedsDisplaymanual), e o SwiftUI entra em um app existente através deNSHostingView,NSGestureRecognizerRepresentable,NSHostingMenueNSHostingSceneRepresentation.8910
As lazy stacks funcionam com base em estimativas
LazyVStack faz o layout de cima para baixo e para assim que o retângulo visível é preenchido, estimando o resto.
A primeira coisa a internalizar sobre uma lazy stack é que ela troca correção por eficiência de propósito. Diferente de um VStack, um LazyVStack não avalia nem renderiza views que não estão visíveis; ele faz o layout das suas views de cima para baixo e para assim que o retângulo visível é preenchido, adicionando views à medida que elas entram pela rolagem e removendo-as à medida que saem.1 O ganho é óbvio. O custo é sutil: como a stack nunca carrega todas as views, as alturas das views fora da tela são estimadas a partir da média do que veio antes, a largura ideal colapsa para a largura da primeira subview, e o espaço acima da região visível é, ele próprio, aproximado.1
Essa estimativa não é um bug que você contorna uma vez; é o substrato sobre o qual toda outra decisão de lazy stack se apoia. A sessão 321 torna a consequência concreta com uma mudança de orientação. Gire um iPhone e a view visível mais ao topo permanece ancorada, mas a stack ainda não mediu o layout exato e novo das views acima dela. Role de volta para o topo e a stack precisa reconciliar: ela corrige o espaço estimado acima da região visível e atualiza o offset do conteúdo da scroll view pela mesma quantidade, de modo que o offset do conteúdo no topo termine em zero.1 A lazy stack e a scroll view que a envolve coordenam posição e offset com precisão, para que, à medida que as estimativas são atualizadas, a posição relativa das subviews visíveis nunca dê um salto.1
O corolário acionável é uma regra sobre em quais APIs de rolagem confiar. Como o offset absoluto do conteúdo é estimado, lê-lo (com .onScrollGeometryChange, digamos, para esconder um botão depois de 100 pontos) dá a você um limiar que se desloca conforme as estimativas se acomodam.2 O sinal estável é a visibilidade relativa. O modifier .onScrollTargetVisibilityChange dispara quando o conjunto de subviews visíveis na scroll view muda, então um botão de “rolar para destacar” pode atrelar a sua visibilidade a quais linhas estão na tela, com um limiar (a sessão usa 80%), em vez de uma contagem instável de pixels.21 A mesma lógica condena o .scrollTransition: uma transformação que empurra uma view para fora do seu frame original pode fazer a stack acreditar que uma view visível está fora da tela e descartá-la cedo demais, então qualquer transição de rolagem precisa impedir que views que normalmente não estariam visíveis sejam empurradas para dentro do retângulo visível.111
Por que suas structs de view não são as subviews
As subviews que uma lazy stack carrega não mapeiam um para um nas structs de view que você escreveu. Um ForEach de StepView resolve para um StepView por passo, mas se o body de cada StepView retorna duas views de nível superior (um diagrama e instruções) sem nenhum layout que as envolva, a stack carrega cada uma delas separadamente.1 O número que importa para a stack é a contagem de subviews resolvidas, não a contagem de structs.
A armadilha é uma contagem de subviews dinâmica. Se um StepView retorna uma subview ou zero dependendo de um valor de ambiente, a stack não pode mais confiar nos índices, porque a contagem de views anteriores poderia mudar. Então ela mantém instâncias anteriores de StepView vivas só por garantia, o que significa que uma mudança de ambiente não relacionada pode disparar avaliações de body para views que saíram da tela pela rolagem, e a stack não libera o estado delas.1 A correção é mover o filtro para fora da view e para cima dos dados: se você usa SwiftData, coloque a condição em um Predicate na Query, para que a contagem de subviews seja conhecida sem construir nenhuma view.1 Desempacotar um opcional em um body tem o mesmo efeito de manter as views vivas por mais tempo; a jogada mais limpa é mostrar um ContentUnavailableView mais acima, em vez de deixar a lazy stack segurar linhas parcialmente resolvidas.1
O prefetching é o mecanismo que faz as estimativas parecerem rápidas. Durante a rolagem, uma scroll view tem apenas até o prazo do frame para atualizar o offset, renderizar as views e executar o seu trabalho de mudança de offset; se exibir uma nova view estourar esse orçamento, o frame é descartado e você vê um engasgo.1 Para evitar isso, a lazy stack verifica se há tempo de sobra e, se houver, faz parte do trabalho de uma view que está prestes a aparecer com antecedência (avaliando o body e o layout dela, e até distribuindo um LazyHStack aninhado ao longo de vários frames), de modo que, quando a view aparecer, a maior parte do trabalho já estará feita.1 É por isso que a sessão é enfática sobre o onAppear: se você configura uma view no onAppear, você descarta o trabalho de prefetch e força um refazimento quando ela aparece, às vezes trazendo mais views do que o necessário e degradando a rolagem. Configure a view no inicializador dela para que ela chegue em um estado razoável, e reserve o onAppear para trabalho genuinamente ligado à aparição, como buscar a próxima página em uma rolagem infinita.1 Em uma rolagem invertida, um body pode até rodar durante o prefetching enquanto o onAppear nunca dispara de fato.1
Gráficos avançados são apenas um pipeline
A sessão 322 reenquadra “gráficos avançados” como composição. Cada modifier do SwiftUI é um tubo que recebe dados, os transforma e os repassa adiante; o resultado avançado está em como você conecta os tubos, não em nenhuma API complexa isolada.3 A sessão constrói uma view de letras ao vivo no estilo do Apple Music encadeando estágios comuns: desfocar a capa do álbum para que ela recue, rodar um shader sobre ela, dirigir esse shader com o tempo e sincronizar a rolagem de uma transcrição à mesma fonte de tempo.3
O estágio do shader é onde está a escolha de verdade. O SwiftUI chama funções de shader Metal através de três pontos de entrada de efeito que sobem em capacidade. O colorEffect transforma a cor de cada pixel a partir da posição e da cor original dele, o que basta para algo como uma conversão para tons de cinza.4 Já o distortionEffect mapeia uma posição para outra (você diz ao SwiftUI para amostrar a cor desta posição a partir daquela posição), o que lida com distorções geométricas sem nenhuma cor envolvida.5 O layerEffect é o mais flexível: ele entrega ao shader a layer inteira da view, de modo que o pixel de saída pode amostrar os seus vizinhos ou toda a região, que é exatamente do que o blur e as distorções mais ricas precisam.63
O fundo de domain-warp da sessão usa o layerEffect. Um offset float2 uniforme desloca cada pixel pela mesma quantidade, o que apenas desliza a imagem; o movimento orgânico precisa de variação por pixel, então o shader amostra uma NoiseTexture pré-computada (passada como uma imagem, chegando do lado do Metal como uma texture2d) cujos canais vermelho e verde fornecem um offset X e Y diferente em cada coordenada UV.3 Amostrar o ruído uma vez distorce a imagem; amostrá-lo duas vezes, a segunda em uma posição que a primeira amostra deslocou, produz bolhas que fluem. Essa técnica de segunda ordem é o domain warping, e a sessão aponta para o seu app de exemplo disponível para download com uma pré-visualização ao vivo dos parâmetros.3
Dois fatos do framework fazem a animação funcionar. Os shaders não têm estado: eles não guardam nenhuma memória do frame anterior, e a saída depende apenas dos parâmetros que você passa.3 Então o movimento não pode vir de dentro do shader; ele precisa ser alimentado, e um TimelineView é o tubo que o fornece, disparando a cada frame com um timestamp em um schedule de animação.73 Passe esse timestamp para o shader, some-o à posição de amostra do ruído, e o padrão flui. O lado da transcrição reutiliza a mesma fonte de tempo na direção oposta: o timestamp de reprodução seleciona a linha atual (em negrito e nítida, o resto esmaecido), e um onChange mantém essa linha centralizada conforme o tempo avança.123 O timestamp flutuante na linha ativa é posicionado não com offset (que precisaria dos tamanhos das duas views), mas com uma sobrescrita de alignment-guide que redefine semanticamente o ponto de um alinhamento, de modo que a borda superior da subview se prenda à borda inferior do seu contêiner sem um offset manual.133
O SwiftUI entra em um app AppKit ou UIKit
A sessão de interoperabilidade abre com um ponto que reenquadra toda a questão da adoção: a maioria dos apps já usa SwiftUI implicitamente. No novo design, controles do AppKit como NSSlider, NSSwitch e NSSegmentedControl são renderizados com SwiftUI por baixo dos panos, e o Liquid Glass compartilha grandes partes da sua implementação entre frameworks também através do SwiftUI.8 Então “adotar o SwiftUI” é menos uma reescrita e mais uma decisão sobre onde tornar a costura explícita.
O primeiro passo não precisa de SwiftUI nenhum. O AppKit e o UIKit agora observam tipos @Observable automaticamente: marque uma classe de modelo como @Observable, leia as propriedades dela dentro de um método de desenho como drawKnob, e o AppKit rastreia cada acesso e redesenha quando qualquer propriedade acessada muda, aposentando o needsDisplay = true manual que você costumava escrever sempre que o valor de um slider afetava a aparência de outro.814 A observação se estende além do draw(_:) para updateConstraints(), layout(), updateLayer() e os equivalentes em NSViewController, e o UIKit alcança ainda mais longe, chegando a UIButton, UICollectionViewCell e outros.8 Ela vem ativada por padrão nas versões de 2026 e é retro-implantável para o macOS 15 (NSObservationTrackingEnabled) e o iOS 18 (UIObservationTrackingEnabled) via Info.plist.8
Uma vez que o modelo é @Observable, a costura de fato com o SwiftUI é pequena. A sessão reconstrói um seletor de cores baseado em slider como um controle SwiftUI circular desenhado com Canvas (uma API de modo imediato análoga a drawRect, com withCGContext para reaproveitar código Core Graphics existente), reutilizando o mesmíssimo @Observable ColorModel.158 Para incorporá-lo onde o AppKit espera uma view, envolva-o em um NSHostingView, uma subclasse de NSView; como o modelo já dirige as atualizações, esse envolvimento é tudo o que é necessário.168 O código de gestos existente é levado adiante sem reescrita: um ForceClickGestureRecognizer chega a uma view SwiftUI através de NSGestureRecognizerRepresentable (implemente makeNSGestureRecognizer e handleNSGestureRecognizerAction), depois se anexa com o modifier comum .gesture e coexiste com o próprio gesto de arrastar do SwiftUI.178 A mesma família representable inclui NSViewRepresentable para incorporar NSViews na direção oposta.8
A costura escala até menus e cenas. Uma View SwiftUI contendo um Button e um Picker se torna um menu de verdade através de NSHostingMenu (uma subclasse de NSMenu), definida como o submenu de um NSMenuItem adicionado ao menu principal, com keyboardShortcut dando à ação um caminho que não depende de gesto para dispositivos de entrada que não conseguem fazer force-click.188 Cenas SwiftUI inteiras também se anexam: um MenuBarExtra chega a um app existente através de NSHostingSceneRepresentation, adicionado via addSceneRepresentation em applicationWillFinishLaunching, com o Toggle de uma cena Settings controlando se o extra é inserido, e a ação de ambiente openSettings() abrindo as configurações a partir de um @IBAction.198 O ponto de encerramento da sessão é o que sustenta tudo: cada API que ela aborda é lançada nas versões de 2026 ou antes, e não há expectativa de que um app seja inteiramente SwiftUI para se beneficiar.8 A pressão pela modernização tem uma borda mais dura em outro ponto do ciclo, porém: o iOS 27 torna o ciclo de vida baseado em cenas do UIKit um requisito de lançamento, de modo que um app que você reconstrói com o SDK mais recente falha ao ser lançado se nunca adotou cenas.
O que a equipe do SwiftUI acrescentou nos labs
Dois esclarecimentos dos group labs da equipe de UI Frameworks da WWDC26 afinam o modelo de invalidação que as sessões descrevem. Ambos são parafraseados de uma gravação transcrita localmente; a Apple não publica legendas oficiais para os labs, a mesma lacuna de legendas que percorreu o próprio Group Lab da equipe do Swift, onde os engenheiros da linguagem responderam às perguntas sobre concorrência e roadmap.
Mover código de fora do body para uma propriedade computada não traz nenhum benefício de invalidação. O SwiftUI ainda reexecuta a propriedade sempre que reexecuta o body, então a mudança é um ganho de legibilidade e nada mais.20 A fronteira de performance aparece um nível acima: extraia um tipo de view separado e o SwiftUI pode invalidá-lo de forma independente, reexecutando apenas aquela view quando as entradas dela mudam, em vez do body inteiro que a envolve. Recorra a uma nova view, não a uma nova propriedade, quando você quiser que o SwiftUI faça menos trabalho.
Toda mudança de ambiente invalida todas as views que leem aquele valor de ambiente. Ler o ambiente é barato; a rotatividade do ambiente não é.21 Mantenha os valores que mudam rápido fora do ambiente (o exemplo do painel é o horário atual), porque um valor que se atualiza a cada frame arrasta todo leitor para uma reavaliação cada vez que ele se move. Passe os valores voláteis pelo caminho que precisa deles e deixe o ambiente carregar as coisas que permanecem paradas.
Uma sessão de lab posterior acrescenta mais três detalhes de mecanismo, todos parafraseados de uma gravação transcrita localmente do SwiftUI Group Lab da WWDC 2026, para o qual a Apple não publica legendas oficiais.
A avaliação parcial do grafo explica para onde o trabalho de prefetch de fato vai. O painel descreveu uma lazy stack avaliando os bodies das células que estão por vir no tempo de frame que sobra depois que o frame atual é renderizado, e então parando logo antes que o próximo frame comece.22 A pegadinha que eles sinalizaram: um onAppear que reconfigura uma célula de modo que ela precise passar pelo layout de novo, porque muda o tamanho da célula, joga fora esse trabalho de prefetch. A orientação deles foi fazer o trabalho de dimensionamento no inicializador da célula, não no body nem no onAppear, para que o prefetch sobreviva.22
O modelo mental da interoperabilidade veio de um participante do painel com mais de uma década no UIKit. O UIKit faz o layout de cima para baixo, da janela para dentro em direção às folhas, enquanto o SwiftUI constrói de baixo para cima, do nó mais interno para fora.22 Quando os dois se intercalam, o painel chamou o arranjo de camadas alternadas de sanduíche ou bolo, que é o motivo pelo qual a intercalação profunda fica sutil: cada framework quer dirigir o passo de layout a partir da extremidade oposta.22
O ponto sobre o ambiente acima ganhou um exemplo vívido do mundo real. Perguntados sobre o pior uso indevido de ambiente que já viram, o painel citou colocar a posição de rolagem no ambiente, que se atualiza a cada frame durante a rolagem e, assim, invalida todo leitor a cada frame.22
Uma segunda sessão do SwiftUI Group Lab acrescentou mais quatro detalhes de mecanismo, todos parafraseados de uma gravação transcrita localmente do SwiftUI Group Lab da WWDC 2026 (sessão 2); a Apple não publica legendas oficiais para os labs.
O modifier onGeometryChange(for:of:action:) se lê melhor do que parece por causa de como as suas duas closures dividem o trabalho. A closure de transformação roda com a geometria ao vivo a cada frame, mas apenas o valor que ela retorna decide se a ação dispara, já que o tipo do resultado é Equatable e a ação só roda quando esse valor muda.23 Então retornar um valor grosseiro (um intervalo de tamanho ou um breakpoint de layout, não o tamanho bruto) converte um sinal na frequência de frames em um que dispara duas vezes, nos limiares, em vez de continuamente. O painel emparelhou isso com um aviso de que o GeometryReader é caro para as subviews que ele envolve e deveria ser confinado a um background para que meça sem dirigir o layout principal.23
Uma boa dynamic property pode substituir a maior parte do trabalho de onChange por completo. O método update() de uma DynamicProperty roda imediatamente antes do body da view, então um property wrapper personalizado pode entregar um valor já em cache (o exemplo do painel foi uma imagem) de forma síncrona nesse ponto e pular a ida e volta do onAppear e o re-render que ele dispara.24 O enquadramento do painel foi que a maior parte dos usos de onChange pode ser substituída por uma dynamic property bem construída.24
Uma view que desenha fora dos limites de layout que ela relata, dentro de uma ScrollView, pode ser descartada (culled), porque o sistema decide que ela está fora da tela com base nos limites que lhe foram informados, não em onde ela de fato pinta.25 O painel nomeou isso como o comportamento de falha concreto por trás de dropdowns personalizados e overlays que transbordam a sua view hospedeira e então somem no meio da rolagem, e observou que o mesmo risco se aplica a conteúdo de overlay que se estende para além da sua âncora.25
Apresentar um overlay em tela cheia acima de tudo, inclusive de sheets, não tem uma resposta limpa puramente em SwiftUI. A orientação do painel foi descer para uma nova UIWindow através do ciclo de vida baseado em cenas do UIKit, com o princípio mais profundo de que a última janela vence e que precisa haver uma única fonte de verdade para o que fica por cima.26 Eles acrescentaram que a melhor correção, com frequência, é restaurar a navigation stack que o usuário espera, em vez de jogar uma capa disruptiva por cima de tudo.26
O que adotar primeiro
Um lançamento explicado como mecanismo recompensa quem ordena por alavancagem, não por novidade.
- Troque as classes de modelo para
@Observableno seu código AppKit/UIKit. Isso elimina chamadas manuais deneedsDisplay, dá a todo método de desenho e layout que observa um redesenho automático, e é o pré-requisito que torna trivial umNSHostingViewde encaixe posterior.814 É a jogada de menor risco e maior retorno imediato em todas as três sessões. - Audite as lazy stacks em busca de contagens dinâmicas de subviews. Qualquer folha de
ForEachque retorne condicionalmente zero ou uma subview, ou que desempacote um opcional no seubody, está mantendo as views vivas por índice; mova o filtro para umPredicateem umaQuery(ou mais acima na hierarquia) e tanto a memória quanto a performance de rolagem-até-o-item melhoram.1 - Mova a configuração de views para fora do
onAppeare para dentro dos inicializadores. O prefetching só ajuda se o trabalho de prefetch sobreviver; uma configuração que muta o tamanho ou o conteúdo noonAppearjoga esse trabalho fora.1 É um ganho silencioso e amplo de suavidade na rolagem. - Substitua as leituras absolutas de offset de rolagem por APIs de visibilidade relativa. Qualquer coisa atrelada ao offset do conteúdo vai se deslocar; o
.onScrollTargetVisibilityChangese atrela a quais linhas estão de fato visíveis.21 - Recorra a shaders apenas onde um pequeno efeito justifica o seu lugar. Comece pelo
colorEffectou pelodistortionEffect; escale para olayerEffectsó quando um efeito precisar amostrar vizinhos, e dirija qualquer movimento com um timestamp deTimelineViewem vez de esperar estado dentro do shader.4567
O fio condutor entre as três: preveja o framework antes de pressioná-lo. As lazy stacks estimam, os shaders esquecem, e a interoperabilidade é uma costura, não uma reescrita. Construa com esses três fatos em mente e o resto segue.
FAQ
Por que a posição de rolagem da minha lazy stack do SwiftUI salta ou se desloca?
Porque um LazyVStack não carrega as views fora da tela, ele estima as alturas delas e o espaço acima da região visível, então o offset absoluto do conteúdo é uma estimativa que o framework corrige conforme aprende o layout real (depois de uma mudança de orientação, por exemplo, ele reconcilia a estimativa quando você rola de volta para o topo).1 Se você atrela a UI ao offset absoluto, o limiar se desloca conforme as estimativas se acomodam. Use o .onScrollTargetVisibilityChange, que dispara com base em quais subviews estão de fato visíveis.2
Como mantenho a rolagem suave em uma lazy stack do SwiftUI no iOS 27?
Deixe o prefetching fazer o seu trabalho: configure as views no inicializador delas para que o trabalho que a lazy stack realiza antes de uma view aparecer não seja descartado, e evite mutar o tamanho ou o conteúdo de uma view no onAppear.1 Evite também um número dinâmico de subviews em folhas de ForEach e evite mudanças de layout (como uma altura dirigida por onGeometryChange) depois que uma view aparece, já que ambos forçam a stack a refazer trabalho ou recalcular posições no meio da rolagem.1
Quando devo usar colorEffect, distortionEffect ou layerEffect?
Use o colorEffect para transformar a cor de cada pixel a partir da posição e da cor original dele (um filtro de tons de cinza, por exemplo).4 Use o distortionEffect para efeitos geométricos, em que você mapeia uma posição de saída para uma posição de origem da qual amostrar.5 Use o layerEffect quando o pixel de saída depende de mais de um pixel de entrada, porque ele dá ao shader a layer inteira da view para amostrar vizinhos ou a região toda, que é o que o blur e o domain warping precisam.6
Como animo um shader Metal no SwiftUI?
Os shaders não têm estado: eles não guardam nenhuma memória do frame anterior e dependem apenas dos seus parâmetros, então você não pode animar de dentro do shader.3 Alimente um valor que muda ao longo do tempo. Um TimelineView em um schedule de animação dispara a cada frame com um timestamp; passe esse timestamp para o shader como parâmetro (a sessão o soma à posição de amostra do ruído) e o efeito anima.73
Posso adicionar SwiftUI a um app AppKit ou UIKit existente sem reescrevê-lo?
Sim, e a sessão é explícita em afirmar que nenhum app precisa ser inteiramente SwiftUI para se beneficiar.8 Marque o seu modelo como @Observable para que o AppKit e o UIKit redesenhem automaticamente, depois incorpore views SwiftUI com NSHostingView, traga os gesture recognizers existentes com NSGestureRecognizerRepresentable, construa menus com NSHostingMenu e anexe cenas SwiftUI a partir do seu app delegate com NSHostingSceneRepresentation; tudo isso é lançado nas versões de 2026 ou antes.816171819
O cluster completo do Apple Ecosystem: o substrato do SwiftUI (result builders, tipos opacos, a árvore de views com tipo de valor) que explica por que uma lazy stack resolve structs de view em um conjunto diferente de subviews; a superfície do SwiftUI no iOS 27 (reordenação, documentos, toolbars, erros) ao lado da qual esta história de performance-e-interoperabilidade se posiciona; os internals do @Observable que agora dirigem o redesenho automático também no AppKit e no UIKit; e os padrões do Liquid Glass cuja implementação entre frameworks a sessão de interoperabilidade atribui ao SwiftUI compartilhado. O hub é a Apple Ecosystem Series. Para um contexto mais amplo de iOS-com-agentes-de-IA, veja o guia de iOS Agent Development.
Referências
-
Apple, WWDC26 sessão 321, “Dive into lazy stacks and scrolling with SwiftUI.” developer.apple.com/videos/play/wwdc2026/321. Aborda o layout de lazy stacks e a estimativa de altura, o offset estimado do conteúdo, a resolução de struct-de-view para subview, a armadilha da contagem dinâmica de subviews, o prefetching ao longo dos prazos de frame e a configuração via
onAppearversus inicializador. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Apple, WWDC26 sessão 321, “Dive into lazy stacks and scrolling with SwiftUI”. A sessão apresenta o
onScrollTargetVisibilityChange(um modifier cuja closure roda quando o conjunto de alvos de rolagem visíveis muda) como a alternativa estável, de visibilidade relativa, às leituras absolutas de offset do conteúdo. ↩↩↩↩↩ -
Apple, WWDC26 sessão 322, “Compose advanced graphics effects with SwiftUI.” developer.apple.com/videos/play/wwdc2026/322. Enquadra os efeitos como um pipeline componível; aborda o blur, os três pontos de entrada de shader, a técnica de domain-warp com
NoiseTexture, os shaders sem estado dirigidos pelo tempo e a fixação por alignment-guide. ↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Documentação para Desenvolvedores da Apple:
colorEffect(_:isEnabled:). Retorna uma nova view que aplica um shader transformando a cor de cada pixel, a partir da posição e da cor original dele. ↩↩↩↩ -
Documentação para Desenvolvedores da Apple:
distortionEffect(_:maxSampleOffset:isEnabled:). Aplica um shader que mapeia a posição de cada pixel para uma posição de origem da qual amostrar, para efeitos geométricos. ↩↩↩↩ -
Documentação para Desenvolvedores da Apple:
layerEffect(_:maxSampleOffset:isEnabled:). Aplica um shader como efeito de layer com acesso à layer inteira da view, permitindo que cada pixel de saída amostre múltiplos pixels de entrada. ↩↩↩↩ -
Documentação para Desenvolvedores da Apple:
TimelineView. Uma view que atualiza o seu conteúdo de acordo com um schedule; em um schedule de animação, ela fornece o timestamp por frame que a sessão 322 alimenta no seu shader. ↩↩↩↩ -
Apple, WWDC26 sessão 272, “Use SwiftUI with AppKit and UIKit.” developer.apple.com/videos/play/wwdc2026/272. Aborda o redesenho automático por
@Observableno AppKit/UIKit, a retro-implantação da observação via Info.plist, oCanvas, oNSHostingView, oNSGestureRecognizerRepresentable, oNSHostingMenue oNSHostingSceneRepresentation. ↩↩↩↩↩↩↩↩↩↩↩↩↩↩↩ -
Documentação para Desenvolvedores da Apple:
NSGestureRecognizerRepresentable. Um protocolo que envolve umNSGestureRecognizerpara uso como gesto SwiftUI, implementado commakeNSGestureRecognizerehandleNSGestureRecognizerAction. ↩ -
Documentação para Desenvolvedores da Apple:
MenuBarExtra. Uma cena que renderiza um item da barra de menus; a sessão 272 a anexa a um app AppKit existente através deNSHostingSceneRepresentation. ↩ -
Documentação para Desenvolvedores da Apple:
scrollTransition(_:axis:transition:). Aplica uma transição conforme uma view rola dentro de uma scroll view; a sessão 321 avisa que uma transformação que empurra uma view para dentro do retângulo visível pode dessincronizar uma lazy stack. ↩ -
Documentação para Desenvolvedores da Apple:
onChange(of:initial:_:). Executa uma ação quando um valor muda; a sessão 322 a usa para recentralizar a linha atual da transcrição. ↩ -
Documentação para Desenvolvedores da Apple:
alignmentGuide(_:computeValue:). Define o alignment guide de uma view para que o sistema de layout a posicione semanticamente; a sessão 322 sobrescreve um guia inferior para prender a borda superior de uma subview à borda inferior do seu contêiner. ↩ -
Documentação para Desenvolvedores da Apple:
Observable. A macro que faz as propriedades mutáveis de uma classe participarem do sistema de Observation, que o AppKit e o UIKit rastreiam para o redesenho automático nas versões de 2026. ↩↩ -
Documentação para Desenvolvedores da Apple:
Canvas. Uma view de desenho em modo imediato cuja closure recebe umGraphicsContext; a sessão 272 a usa para redesenhar o seletor de cores circular e observa owithCGContextpara reaproveitar código Core Graphics. ↩ -
Documentação para Desenvolvedores da Apple:
NSHostingView. Uma subclasse deNSViewque hospeda uma hierarquia de views SwiftUI dentro de uma árvore de views do AppKit. ↩↩ -
Documentação para Desenvolvedores da Apple:
NSViewRepresentable. Um wrapper que permite a umaNSViewparticipar de uma hierarquia de views SwiftUI; a sessão 272 a nomeia ao lado doNSGestureRecognizerRepresentablecomo parte da família representable. ↩↩ -
Documentação para Desenvolvedores da Apple:
NSHostingMenu. Uma subclasse deNSMenuque renderiza uma view SwiftUI como conteúdo de menu, adicionada ao menu principal como o submenu de umNSMenuItem. ↩↩ -
Documentação para Desenvolvedores da Apple:
keyboardShortcut(_:modifiers:). Atribui um atalho de teclado à ação de um controle; a sessão 272 adiciona um ao botão de menu para que dispositivos de entrada que não conseguem fazer force-click ainda alcancem o recurso. ↩↩ -
Apple, group lab de UI Frameworks da WWDC 2026, sessão 8002. Parafraseado de uma gravação transcrita localmente; nenhuma transcrição oficial é publicada. A equipe esclareceu que mover código do
bodypara uma propriedade computada é apenas uma mudança de legibilidade, e que a fronteira de invalidação independente aparece quando você extrai um tipo de view separado. ↩ -
Apple, group lab de UI Frameworks da WWDC 2026, sessão 8003. Parafraseado de uma gravação transcrita localmente; nenhuma transcrição oficial é publicada. A equipe observou que toda mudança de ambiente invalida todas as views que leem aquele valor, e aconselhou manter os valores que mudam rápido (o exemplo do painel foi o horário atual) fora do ambiente. ↩
-
Apple, WWDC26 sessão 8006, “SwiftUI Group Lab.” developer.apple.com/videos/play/wwdc2026/8006. Parafraseado de uma gravação transcrita localmente do SwiftUI Group Lab da WWDC 2026; a Apple não publica legendas oficiais para os labs. Fonte para a avaliação parcial do grafo no tempo de frame que sobra (e o aviso do
onAppear-com-redimensionamento, com o dimensionamento feito no init), o arranjo de interoperabilidade “sanduíche ou bolo” do UIKit-de-cima-para-baixo-versus-SwiftUI-de-baixo-para-cima, e o exemplo da posição-de-rolagem-no-ambiente que invalida todo leitor a cada frame. ↩↩↩↩↩ -
Apple, SwiftUI Group Lab da WWDC 2026 (sessão 2). Parafraseado de uma gravação transcrita localmente do SwiftUI Group Lab da WWDC 2026 (sessão 2); a Apple não publica legendas oficiais para os labs. Fonte para o mecanismo de transformação-decide-a-ação do
onGeometryChange(retorne um valor grosseiro para disparar nos limiares) e a orientação de confinar umGeometryReadercaro a um background. O formato de duas closures está documentado emonGeometryChange(for:of:action:): a closure de transformaçãoof:deriva um valorEquatabledo geometry proxy, e a closureaction:roda apenas quando esse valor muda. ↩↩ -
Apple, SwiftUI Group Lab da WWDC 2026 (sessão 2). Parafraseado de uma gravação transcrita localmente do SwiftUI Group Lab da WWDC 2026 (sessão 2); a Apple não publica legendas oficiais para os labs. Fonte para substituir a maior parte do trabalho de
onChangepor uma dynamic property que entrega um valor em cache de forma síncrona. O protocoloDynamicPropertyda Apple define um métodoupdate()que o SwiftUI chama imediatamente antes de renderizar obodyde uma view, para que a propriedade contenha o seu valor mais recente. ↩↩ -
Apple, SwiftUI Group Lab da WWDC 2026 (sessão 2). Parafraseado de uma gravação transcrita localmente do SwiftUI Group Lab da WWDC 2026 (sessão 2); a Apple não publica legendas oficiais para os labs. Fonte para o descarte (culling) de views que desenham fora dos limites de layout que relatam dentro de uma
ScrollView(a falha por trás de dropdowns e overlays personalizados que transbordam), e a observação de que o mesmo risco se aplica a conteúdo deoverlayque se estende para além da sua âncora. ↩↩ -
Apple, SwiftUI Group Lab da WWDC 2026 (sessão 2). Parafraseado de uma gravação transcrita localmente do SwiftUI Group Lab da WWDC 2026 (sessão 2); a Apple não publica legendas oficiais para os labs. Fonte para a ausência de uma resposta limpa puramente em SwiftUI para um overlay em tela cheia acima de sheets, a descida para uma nova
UIWindowatravés do ciclo de vida baseado em cenas do UIKit, o princípio “a última janela vence / fonte única de verdade”, e a preferência por restaurar a navigation stack em vez de uma capa disruptiva. ↩↩