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() é 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.
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.
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.
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.
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ística | setNeedsDisplay() | setNeedsDisplay(_:) |
|---|---|---|
| Parâmetro | Nenhum | CGRect — área de redesenho |
| Área de redesenho | Todos os bounds da view | Apenas o retângulo especificado |
| Desempenho | Menor para views grandes | Maior para alterações parciais |
| Parâmetro rect em draw(_:) | Todos os bounds | Retângulo especificado |
| Quando usar | Alteração de conteúdo completo | Alteraçõ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() é 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.
// 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(_:).
Chamadas frequentes a setNeedsDisplay() podem degradar o desempenho, especialmente durante animações. Vamos analisar as principais estratégias de otimização.
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.
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.
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.
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
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.
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.
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.
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.
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
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.
Leia também