Para corrigir o conteúdo do SwiftUI que sai da tela durante o redimensionamento de uma janela do Mac, primeiro identifique se o desenho ultrapassa os limites do layout, se o conteúdo se recusa a encolher ou se uma visualização AppKit incorporada deixa de acompanhar seu contêiner. Em seguida, corrija esse limite específico.

Este guia destina-se a desenvolvedores que corrigem a interface redimensionável do próprio app para Mac: mantendo rótulos legíveis, controles acessíveis e conteúdos mais longos roláveis. A sequência de diagnóstico abaixo usa rótulos sintéticos e distingue o layout do SwiftUI, o dimensionamento da janela e a quebra de texto do AppKit.

Identifique qual limite falha durante o redimensionamento

O layout do SwiftUI começa com uma proposta de tamanho enviada por uma visualização pai. A visualização filha escolhe seu tamanho em resposta; a proposta não é uma instrução para recortar tudo nesse retângulo. A Apple documenta propostas de tamanho mínimo, máximo e ideal em ProposedViewSize. Isso explica por que tornar uma janela mais estreita não necessariamente torna todos os descendentes mais estreitos.

Comece com um teste reproduzível: abra a janela do app, estreite-a até surgir a primeira falha e, em seguida, alargue-a novamente. Observe o primeiro elemento afetado, não a disposição final. Um rótulo se estende para fora do contêiner? O painel inteiro se desloca? O texto mantém a largura de quebra de linha anterior?

Adicione temporariamente .border(Color.orange) à visualização filha suspeita e .border(Color.blue) ao quadro ou contêiner que a envolve. Coloque os modificadores em níveis diferentes para comparar seus retângulos. A documentação do offset da Apple demonstra essa técnica de bordas para distinguir a posição original no layout do desenho deslocado.

O que você observa O que inspecionar primeiro Próxima ação útil
O desenho sai de um retângulo estável offset, position, cálculos de coordenadas Remova os modificadores de posicionamento um de cada vez
Um rótulo continua largo enquanto seu contêiner se estreita Uso horizontal de fixedSize, larguras explícitas Permita que o rótulo aceite uma largura restrita
Os controles não cabem em uma linha horizontal Larguras combinadas dos controles e espaçamento Ofereça uma disposição vertical alternativa
O conteúdo inferior desaparece quando a altura diminui Ausência de comportamento para conteúdo excedente Torne a região de conteúdo rolável
Um painel SwiftUI hospedado se desloca dentro do AppKit Limites da visualização hospedeira e restrições de tamanho Compare o tamanho da hospedeira com os requisitos do conteúdo
Uma visualização de texto AppKit se estreita, mas as linhas continuam largas Acompanhamento da largura do contêiner de texto Verifique a configuração da visualização e do contêiner de texto

Estas são hipóteses de diagnóstico derivadas dos comportamentos documentados, não uma garantia de que um sintoma específico tenha uma única causa. Altere uma suspeita por vez e repita o mesmo gesto de redimensionamento. Se substituir uma visualização filha complexa por Text("Sample panel") eliminar a falha, restaure gradualmente as camadas dessa visualização.

Remova regras de posicionamento e dimensionamento que resistem à janela

Verifique offsets e posicionamento baseado no centro

Um offset desloca o conteúdo exibido sem alterar as dimensões originais da visualização. Portanto, um offset horizontal positivo pode empurrar o desenho para além da borda direita, embora o pai ainda reserve o espaço original. Remova o offset temporariamente; se o desenho voltar para dentro do contêiner, substitua o ajuste por uma disposição expressa com pilhas, preenchimento ou alinhamento do quadro. Consulte o comportamento de offset da Apple.

position(x:y:) posiciona o centro da visualização em coordenadas no espaço de coordenadas do pai. Tratar essas coordenadas como o canto superior esquerdo introduz um erro relacionado ao tamanho da visualização filha. Uma posição calculada uma única vez também pode deixar de ser adequada quando o tamanho do pai muda. Para um painel comum com rótulos e controles, prefira um alinhamento que expresse a relação desejada com o contêiner. A Apple define o comportamento baseado no centro em position(x:y:).

Por exemplo, .frame(maxWidth: .infinity, alignment: .leading) expressa o alinhamento à borda inicial em um quadro flexível. Isso não elimina os requisitos de dimensionamento de uma visualização filha grande demais. A documentação de quadro flexível da Apple explica como o quadro restringe as propostas e posiciona sua visualização filha.

Verifique larguras fixas e o tamanho ideal do texto

Inspecione as configurações de largura explícita e o uso horizontal de fixedSize no ramo que falha. A Apple documenta especificamente que .fixedSize(horizontal: true, vertical: false) faz o texto preservar a largura ideal necessária para a string inteira, podendo exceder os limites do pai. Para texto corrido com quebra de linha, remova esse requisito horizontal. Quando for adequado preservar toda a altura do texto quebrado, considere .fixedSize(horizontal: false, vertical: true). Consulte fixedSize(horizontal:vertical:).

Preservar o tamanho vertical exige um espaço para essa altura. Um parágrafo que cresce verticalmente dentro de um contêiner baixo e sem rolagem ainda pode ficar inacessível. Use uma região de rolagem vertical quando a tarefa exigir acesso a todo o texto; a Apple documenta os eixos de rolagem compatíveis em ScrollView.

Verifique novamente os cálculos baseados em geometria

GeometryReader define o conteúdo usando seu próprio tamanho e espaço de coordenadas, e retorna ao pai um tamanho preferencial flexível. Adicioná-lo ao redor de uma visualização altera a estrutura do layout, portanto ele não é um contêiner neutro de medição. Se o deslocamento começou após a introdução de cálculos geométricos, substitua temporariamente esse ramo por pilhas e quadros comuns. A Apple descreve essas propriedades em GeometryReader.

Para um layout proporcional que realmente precise de geometria, derive as dimensões do contêiner atual relevante. Inspecione qualquer estado que armazene uma largura capturada na aparição inicial. Verifique também se a dimensão medida é usada para redimensionar a mesma região que está sendo medida; remover essa dependência é um teste útil de isolamento.

Crie um layout compacto com comportamento explícito para conteúdo excedente

O exemplo completo a seguir demonstra três escolhas independentes: o texto quebra linhas, os controles mudam de disposição quando necessário e o conteúdo que excede a altura permanece acessível. Suas dimensões são valores ilustrativos de design, não valores prescritos pela Apple.

Crie um app SwiftUI para macOS no Xcode, defina o destino de implantação como macOS 13 ou posterior e substitua as declarações geradas do app e da visualização de conteúdo por este único arquivo. Mantenha apenas uma declaração @main. O exemplo usa as APIs ViewThatFits e de redimensionamento de janelas disponíveis no macOS 13. A Apple documenta o comportamento delas em ViewThatFits e windowResizability(_:).

import SwiftUI

@main
struct ResizeExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ResizeExampleView()
                .frame(minWidth: 280, minHeight: 220)
        }
        .windowResizability(.contentMinSize)
    }
}

struct ResizeExampleView: View {
    @State private var selection = "Sample A"

    private let details = Array(
        repeating: "Sample notes wrap as the panel becomes narrower.",
        count: 12
    ).joined(separator: " ")

    var body: some View {
        ScrollView(.vertical) {
            VStack(alignment: .leading, spacing: 16) {
                Text("Sample panel")
                    .font(.title2)

                Text(details)
                    .fixedSize(horizontal: false, vertical: true)
                    .frame(maxWidth: .infinity, alignment: .leading)

                ViewThatFits(in: .horizontal) {
                    HStack(spacing: 12) {
                        Button("Choose Sample A") {
                            selection = "Sample A"
                        }
                        Button("Choose Sample B") {
                            selection = "Sample B"
                        }
                    }
                    .fixedSize(horizontal: true, vertical: false)

                    VStack(alignment: .leading, spacing: 12) {
                        Button("Choose Sample A") {
                            selection = "Sample A"
                        }
                        Button("Choose Sample B") {
                            selection = "Sample B"
                        }
                    }
                }

                Text("Selected: \(selection)")
            }
            .padding(20)
            .frame(
                minWidth: 0,
                maxWidth: .infinity,
                alignment: .leading
            )
        }
    }
}

ViewThatFits avalia as alternativas na ordem declarada e escolhe a primeira cujo tamanho ideal cabe nos eixos restritos. Aqui, o ajuste horizontal escolhe entre a linha de botões e a pilha vertical. A linha preserva deliberadamente seu tamanho ideal horizontal para que uma linha comprimida não se torne a opção preferida. Mantenha ambas as alternativas equivalentes quanto às ações disponíveis. Consulte as regras de seleção da Apple.

O quadro externo permite que a coluna de conteúdo acompanhe a largura disponível, enquanto a visualização de rolagem vertical dá acesso ao conteúdo quando a quebra de linha aumenta a altura. Essa disposição pressupõe que os controles individuais caibam na largura mínima escolhida. Rótulos mais longos ou fontes maiores podem exigir outro mínimo ou uma disposição compacta diferente.

Este exemplo foi inspecionado para verificar a consistência das declarações e a disponibilidade das APIs; não foi compilado aqui porque não havia um compilador Swift disponível. Execute-o no macOS para verificar o comportamento real da janela e do texto.

Defina os limites da janela depois de corrigir o layout do conteúdo

Um tamanho mínimo útil para a janela impede que a interface fique menor do que a disposição compatível. Com .contentMinSize, o SwiftUI deriva o tamanho mínimo da janela do conteúdo e não impõe um tamanho máximo. No exemplo, o quadro raiz fornece os valores mínimos de largura e altura. Consulte contentMinSize.

Escolha esse mínimo testando a disposição compacta com os rótulos compatíveis mais longos. Aumentar o tamanho mínimo até que uma visualização deslocada fique visível pode ocultar o erro de layout original. Primeiro, remova deslocamentos involuntários e visualizações filhas grandes demais; em seguida, defina as menores dimensões nas quais a tarefa pretendida continua utilizável.

Se o AppKit controla a janela e hospeda o SwiftUI, inspecione separadamente o limite da visualização hospedeira. No macOS 13 e posterior, NSHostingView.sizingOptions controla as restrições derivadas do conteúdo do SwiftUI. Quando a hospedeira é a contentView da janela, ela também atualiza os tamanhos mínimo e máximo do conteúdo da janela. A Apple também documenta que o conteúdo fica centralizado quando o quadro da hospedeira difere do tamanho exigido pelo conteúdo SwiftUI. Verifique essas relações antes de adicionar offsets compensatórios. Consulte NSHostingView sizingOptions.

Verifique AppKit e TextKit apenas quando a visualização com falha os utiliza

Permita que o SwiftUI dimensione a visualização raiz representada

Para um NSViewRepresentable, o SwiftUI controla o frame e o bounds da visualização AppKit representada. A Apple alerta explicitamente que definir essas propriedades por conta própria entra em conflito com o SwiftUI e produz comportamento indefinido. Se o seu manipulador de redimensionamento atribui repetidamente o quadro da visualização raiz representada, remova essa disputa de controle e expresse o dimensionamento pelo limite do SwiftUI. Consulte NSViewRepresentable.

Quando uma medição personalizada for necessária, o macOS 13 e posterior oferece sizeThatFits(_:nsView:context:). Ele pode receber várias propostas durante uma passagem de layout; retornar nil solicita o dimensionamento padrão. Faça o resultado depender da proposta atual e do conteúdo, não da largura anterior da janela. Uma dimensão proposta não especificada precisa de uma política de medição intencional, em vez de ser tratada automaticamente como zero. Consulte dimensionamento de representáveis.

O binding opcional do Swift oferece uma maneira de verificar e extrair uma dimensão proposta antes de usá-la. Isso é útil ao implementar uma política de dimensionamento sem forçar o unwrap de larguras ou alturas opcionais. O comportamento da linguagem está documentado em The Basics: Optional Binding.

Separe a largura de quebra de linha da largura da visualização de texto

Se a visualização filha com falha for um NSTextView, compare sua largura atual com a largura do contêiner de texto. A Apple documenta que NSTextContainer.widthTracksTextView tem como padrão false; defini-lo como true faz o contêiner ajustar sua largura quando a largura da visualização de texto muda. Esse é o controle relevante quando as linhas devem refluir com a visualização de texto. Consulte widthTracksTextView.

Inspecione também isHorizontallyResizable. Ele controla se a visualização de texto altera sua largura para caber no texto, um requisito diferente de quebrar o texto dentro de uma largura atribuída. Para uma região de texto com quebra de linha e rolagem vertical, configure essas duas políticas de modo consistente e certifique-se de que o layout contêiner realmente redimensione a visualização de texto. Consulte isHorizontallyResizable.

Preserve o mecanismo de texto usado pela sua implementação. A Apple documenta que acessar layoutManager em um NSTextView ou em seu contêiner de texto pode colocá-lo em modo de compatibilidade usando NSLayoutManager. Não adicione esse acesso casualmente ao diagnosticar uma visualização TextKit 2. Use as APIs apropriadas para o mecanismo existente e o destino de implantação. Consulte NSTextView.

Verifique a correção após mudanças de largura e altura

Use esta sequência final no seu próprio app:

  1. Estreite a janela lentamente até passar pelo ponto da falha original e, em seguida, alargue-a novamente.
  2. Reduza a altura de forma independente e confirme que todo o conteúdo necessário continua acessível.
  3. Repita com rótulos sintéticos longos e texto com várias linhas.
  4. Ative cada controle nas disposições ampla e compacta.
  5. Para uma visualização de texto AppKit, confirme que a quebra de linha acompanha a largura atual nos dois sentidos do redimensionamento.

Uma correção bem-sucedida mantém o posicionamento vinculado ao contêiner atual, oferece ao conteúdo uma disposição compacta funcional e permite acessar o conteúdo excedente. Quando esses comportamentos se mantiverem após redimensionamentos repetidos, remova as bordas temporárias e mantenha o tamanho mínimo da janela justificado pela interface.

Fontes