setNeedsDisplay(): essência do método e mecanismo de redesenho no iOS

Autor: IT Sectr Publicado: 2026-07-20 Tempo de leitura: 6 min

setNeedsDisplay() é um método de instância de UIView que marca a view como precisando ser redesenhada. Ao contrário da chamada direta a draw(_:), setNeedsDisplay() apenas define a flag dirty para a view e retorna o controle imediatamente. O sistema processa esta solicitação no ciclo de renderização mais próximo, permitindo combinar várias chamadas em uma única operação de desenho. De acordo com a Documentação da Apple (2025), o redesenho adiado através de setNeedsDisplay() é a única maneira correta de atualizar o conteúdo de uma UIView personalizada.

Pontos Principais

  • setNeedsDisplay() — uma solicitação adiada de redesenho para UIView, não chama draw(_:) de forma síncrona
  • dirty flag — uma flag interna da view definida por setNeedsDisplay() e limpa após draw(_:)
  • setNeedsDisplay(_:) — uma sobrecarga com CGRect para redesenho parcial apenas da região especificada
  • displayIfNeeded() — redesenho síncrono forçado, usado para atualizações imediatas
  • Ciclo de renderização — o sistema processa as views dirty no final do run loop, combinando várias solicitações em uma única passada

O que é setNeedsDisplay()

setNeedsDisplay() é um método de UIView que sinaliza ao sistema que o conteúdo da view está desatualizado e precisa ser renderizado novamente. O método não recebe parâmetros e retorna void. Após a chamada, a view é marcada como dirty, e no ciclo de desenho mais próximo o sistema chama draw(_:) para esta view.

A principal característica é a assincronicidade. Várias chamadas consecutivas a setNeedsDisplay() para a mesma view não resultarão em múltiplas chamadas a draw(_:) — o sistema as combina em uma única operação. Isso é criticamente importante para o desempenho: se uma propriedade da view mudar várias vezes por frame, draw(_:) é chamado apenas uma vez.

Como funciona o redesenho adiado

O mecanismo de redesenho adiado é baseado no ciclo de renderização do UIKit. Quando setNeedsDisplay() é chamado, o sistema define a flag interna layer.needsDisplay como true. No final do run loop atual, o sistema percorre todas as camadas dirty e chama draw(_:) para cada uma.

Run loop e etapas de renderização

O run loop do UIKit inclui uma fase de atualização durante a qual as views dirty são processadas. Nesta fase, layoutSubviews() é chamado se necessário, em seguida draw(_:) para todas as views marcadas. Após a conclusão do desenho, o resultado é passado para a GPU através do Core Animation para composição.

swift
class ProgressView: UIView {
    var progress: CGFloat = 0 {
        didSet {
            setNeedsDisplay()
        }
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        guard let ctx = UIGraphicsGetCurrentContext() else { return }

        // Fundo
        ctx.setFillColor(UIColor.lightGray.cgColor)
        ctx.fill(rect)

        // Barra de progresso
        let fillRect = CGRect(x: rect.minX, y: rect.minY,
                              width: rect.width * progress, height: rect.height)
        ctx.setFillColor(UIColor.systemGreen.cgColor)
        ctx.fill(fillRect)
    }
}

Neste exemplo, progressView atualiza progress, e cada setter chama automaticamente setNeedsDisplay(). O sistema aguarda o final do run loop e chama draw(_:) com o valor real de progress. Se progress mudar três vezes por frame, draw(_:) executa apenas uma vez — com o último valor.

setNeedsDisplay() vs setNeedsDisplay(_:)

O método setNeedsDisplay(_:) recebe um parâmetro rect do tipo CGRect, limitando a área de redesenho. Isso permite redesenhar apenas a parte modificada da view, em vez da view inteira. A diferença de desempenho torna-se perceptível ao trabalhar com views grandes onde as alterações afetam uma área pequena.

CaracterísticasetNeedsDisplay()setNeedsDisplay(_:)
ParâmetroNenhumCGRect — área de redesenho
Área de redesenhoTodos os bounds da viewApenas o retângulo especificado
DesempenhoMenor para views grandesMaior para alterações parciais
Parâmetro rect em draw(_:)Todos os boundsRetângulo especificado
Quando usarAlteração de conteúdo completoAlterações pontuais (cursor, destaque)

Para gráficos complexos, recomenda-se desenhar toda a cena em um contexto fora da tela primeiro, e então chamar setNeedsDisplay(_:) apenas para a área alterada. Esta é uma técnica padrão em aplicativos de desenho e editores gráficos no iOS.

displayIfNeeded() contra setNeedsDisplay()

displayIfNeeded() é um método síncrono que aciona imediatamente o redesenho de todas as views dirty na hierarquia, sem esperar pelo final do run loop. Ao contrário de setNeedsDisplay(), que apenas define uma flag, displayIfNeeded() força a execução imediata de draw(_:).

A Apple recomenda usar displayIfNeeded() apenas em casos extremos quando o resultado do desenho é necessário imediatamente — por exemplo, antes de tirar uma captura de tela ou durante a geração síncrona de imagens para UIGraphicsImageRenderer. Em cenários normais, o modelo assíncrono através de setNeedsDisplay() é preferível, pois não bloqueia a thread principal.

swift
    // Assíncrono — recomendado
someView.setNeedsDisplay()

    // Síncrono — apenas quando necessário
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) chamado imediatamente

Chamar displayIfNeeded() sem um setNeedsDisplay() anterior não terá efeito — o método só processa views já marcadas como dirty. Se nenhuma view precisar de redesenho, displayIfNeeded() é concluído sem chamar draw(_:).

Otimização de chamadas setNeedsDisplay()

Chamadas frequentes a setNeedsDisplay() podem degradar o desempenho, especialmente durante animações. Vamos analisar as principais estratégias de otimização.

Agrupamento de alterações através de um modelo

Em vez de chamar setNeedsDisplay() a cada alteração de uma propriedade individual, acumule as alterações e chame o método uma vez após aplicar todas as atualizações. Por exemplo, ao alterar a cor, tamanho e posição de um elemento — chame setNeedsDisplay() uma vez após definir todas as propriedades.

Uso de CALayer para propriedades animáveis

Se uma propriedade pode ser animada através de CALayer (backgroundColor, opacity, position, transform), use Core Animation em vez de draw(_:). As propriedades de CALayer são atualizadas na GPU sem chamar draw(_:), fornecendo 60 FPS sem carga na CPU.

Atualização em lote através de CATransaction

Para agrupar várias alterações em uma única passada de renderização, use CATransaction. Isso permite combinar várias chamadas setNeedsDisplay() em uma única operação draw(_:) no final da transação.

swift
CATransaction.begin()
CATransaction.setDisableActions(true)

view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()

CATransaction.commit()

Não abuse de displayIfNeeded() — o redesenho síncrono bloqueia a thread principal e pode causar quedas de quadros. Use-o apenas para operações onde o resultado do desenho é crítico antes do final do run loop atual.

Perguntas Frequentes

Posso chamar draw(_:) diretamente em vez de setNeedsDisplay()?

Não, chamar diretamente draw(_:) é proibido. setNeedsDisplay() marca corretamente a view como dirty, e o sistema chama draw(_:) no momento adequado do ciclo de renderização. Chamar draw(_:) diretamente ignora o mecanismo de cache e pode levar a um estado inconsistente.

O que acontece com múltiplas chamadas a setNeedsDisplay()?

Múltiplas chamadas a setNeedsDisplay() para a mesma view são combinadas pelo sistema em uma única chamada a draw(_:). Isso acontece porque o método apenas define a dirty flag, que é limpa somente após a execução real de draw(_:) no final do run loop.

Como setNeedsDisplay() difere de setNeedsLayout()?

setNeedsDisplay() solicita um redesenho do conteúdo da view (chamando draw(_:)). setNeedsLayout() solicita uma reorganização das subviews (chamando layoutSubviews()). layoutSubviews() pode levar a alterações de tamanho, que por sua vez podem desencadear um redesenho.

Quando usar setNeedsDisplay(_:)?

setNeedsDisplay(_:) deve ser usado para alterações pontuais: movimento do cursor, mudança de cor de uma área selecionada, atualização de parte de um gráfico. Isso limita a área de redesenho e melhora o desempenho em comparação com redesenh a view inteira.

Alterar o frame chama automaticamente setNeedsDisplay()?

Por padrão, alterar o frame ou bounds não chama setNeedsDisplay(). O sistema apenas move a view. Se o redesenho for necessário ao alterar o tamanho, defina a propriedade contentMode como .redraw — então o UIKit chamará automaticamente setNeedsDisplay() em cada alteração de bounds.

Resumo

  • setNeedsDisplay() — um método assíncrono de UIView para solicitar redesenho adiado definindo a dirty flag
  • setNeedsDisplay(_:) — uma sobrecarga com CGRect para redesenho parcial apenas da região especificada, otimizando o desempenho
  • displayIfNeeded() — um método síncrono para redesenho imediato de views dirty, usado apenas quando absolutamente necessário
  • Run loop — o sistema processa as views dirty no final do ciclo atual, combinando várias chamadas em uma única passada de draw(_:)
  • CALayer — para propriedades animáveis (position, opacity) use Core Animation em vez de draw(_:) — é uma ordem de magnitude mais eficiente
  • CATransaction — agrupa várias alterações em um ciclo de renderização, reduzindo o número de chamadas a draw(_:)
  • contentMode = .redraw — ativa a chamada automática de setNeedsDisplay() quando os bounds da view mudam

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também