setNeedsDisplay() es un método de instancia de UIView que marca la vista como necesitada de ser redibujada. A diferencia de llamar directamente a draw(_:), setNeedsDisplay() solo establece el flag dirty para la vista y devuelve el control inmediatamente. El sistema procesa esta solicitud en el ciclo de renderizado más cercano, permitiendo combinar múltiples llamadas en una sola operación de dibujo. Según la Documentación de Apple (2025), el redibujado diferido mediante setNeedsDisplay() es la única forma correcta de actualizar el contenido de un UIView personalizado.
Puntos Clave
setNeedsDisplay() es un método de UIView que señala al sistema que el contenido de la vista está obsoleto y necesita ser renderizado nuevamente. El método no toma parámetros y devuelve void. Después de la llamada, la vista se marca como dirty, y en el ciclo de dibujo más cercano el sistema llama a draw(_:) para esta vista.
La característica clave es la asincronía. Varias llamadas consecutivas a setNeedsDisplay() para la misma vista no resultarán en múltiples llamadas a draw(_:) — el sistema las combina en una sola operación. Esto es críticamente importante para el rendimiento: si una propiedad de la vista cambia varias veces por fotograma, draw(_:) se llama solo una vez.
El mecanismo de redibujado diferido se basa en el ciclo de renderizado de UIKit. Cuando se llama a setNeedsDisplay(), el sistema establece el flag interno layer.needsDisplay en true. Al final del run loop actual, el sistema itera a través de todas las capas dirty y llama a draw(_:) para cada una.
El run loop de UIKit incluye una fase de actualización durante la cual se procesan las vistas dirty. En esta fase, se llama a layoutSubviews() si es necesario, luego draw(_:) para todas las vistas marcadas. Después de completar el dibujo, el resultado se pasa a la GPU a través de Core Animation para el compositing.
class ProgressView: UIView {
var progress: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let ctx = UIGraphicsGetCurrentContext() else { return }
// Fondo
ctx.setFillColor(UIColor.lightGray.cgColor)
ctx.fill(rect)
// Barra de progreso
let fillRect = CGRect(x: rect.minX, y: rect.minY,
width: rect.width * progress, height: rect.height)
ctx.setFillColor(UIColor.systemGreen.cgColor)
ctx.fill(fillRect)
}
}
En este ejemplo, progressView actualiza progress, y cada setter llama automáticamente a setNeedsDisplay(). El sistema espera al final del run loop y llama a draw(_:) con el valor de progress actual. Si progress cambia tres veces por fotograma, draw(_:) se ejecuta solo una vez — con el último valor.
El método setNeedsDisplay(_:) toma un parámetro rect de tipo CGRect, limitando el área de redibujado. Esto permite redibujar solo la parte modificada de la vista, en lugar de la vista completa. La diferencia de rendimiento se vuelve notable al trabajar con vistas grandes donde los cambios afectan un área pequeña.
| Característica | setNeedsDisplay() | setNeedsDisplay(_:) |
|---|---|---|
| Parámetro | Ninguno | CGRect — área de redibujado |
| Área de redibujado | Todos los bounds de la vista | Solo el rectángulo especificado |
| Rendimiento | Menor en vistas grandes | Mayor en cambios parciales |
| Parámetro rect en draw(_:) | Todos los bounds | Rectángulo especificado |
| Cuándo usarlo | Cambio de contenido completo | Cambios puntuales (cursor, resaltado) |
Para gráficos complejos, se recomienda dibujar toda la escena en un contexto fuera de pantalla primero, y luego llamar a setNeedsDisplay(_:) solo para el área modificada. Esta es una técnica estándar en aplicaciones de dibujo y editores gráficos en iOS.
displayIfNeeded() es un método síncrono que desencadena inmediatamente el redibujado de todas las vistas dirty en la jerarquía, sin esperar al final del run loop. A diferencia de setNeedsDisplay(), que solo establece un flag, displayIfNeeded() fuerza la ejecución inmediata de draw(_:).
Apple recomienda usar displayIfNeeded() solo en casos extremos cuando el resultado del dibujo se necesita de inmediato — por ejemplo, antes de tomar una captura de pantalla o durante la generación síncrona de imágenes para UIGraphicsImageRenderer. En escenarios normales, el modelo asíncrono mediante setNeedsDisplay() es preferible ya que no bloquea el hilo principal.
// Asíncrono — recomendado
someView.setNeedsDisplay()
// Síncrono — solo cuando sea necesario
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) llamado inmediatamente
Llamar a displayIfNeeded() sin un setNeedsDisplay() previo no tendrá efecto — el método solo procesa vistas ya marcadas como dirty. Si ninguna vista necesita redibujado, displayIfNeeded() se completa sin llamar a draw(_:).
Las llamadas frecuentes a setNeedsDisplay() pueden degradar el rendimiento, especialmente durante animaciones. Veamos las principales estrategias de optimización.
En lugar de llamar a setNeedsDisplay() en cada cambio de una propiedad individual, acumule los cambios y llame al método una vez después de aplicar todas las actualizaciones. Por ejemplo, al cambiar el color, tamaño y posición de un elemento — llame a setNeedsDisplay() una vez después de establecer todas las propiedades.
Si una propiedad se puede animar mediante CALayer (backgroundColor, opacity, position, transform), use Core Animation en lugar de draw(_:). Las propiedades de CALayer se actualizan en la GPU sin llamar a draw(_:), proporcionando 60 FPS sin carga en la CPU.
Para agrupar múltiples cambios en un solo pase de renderizado, use CATransaction. Esto permite combinar varias llamadas a setNeedsDisplay() en una sola operación draw(_:) al final de la transacción.
CATransaction.begin()
CATransaction.setDisableActions(true)
view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()
CATransaction.commit()
No abuse de displayIfNeeded() — el redibujado síncrono bloquea el hilo principal y puede causar caídas de fotogramas. Úselo solo para operaciones donde el resultado del dibujo sea crítico antes de que termine el run loop actual.
Preguntas Frecuentes
No, llamar directamente a draw(_:) está prohibido. setNeedsDisplay() marca correctamente la vista como dirty, y el sistema llama a draw(_:) en el momento adecuado del ciclo de renderizado. Llamar directamente a draw(_:) ignora el mecanismo de caché y puede llevar a un estado inconsistente.
Múltiples llamadas a setNeedsDisplay() para la misma vista son combinadas por el sistema en una sola llamada a draw(_:). Esto ocurre porque el método solo establece el dirty flag, que se limpia solo después de la ejecución real de draw(_:) al final del run loop.
setNeedsDisplay() solicita un redibujado del contenido de la vista (llamando a draw(_:)). setNeedsLayout() solicita una reorganización de las subvistas (llamando a layoutSubviews()). layoutSubviews() puede provocar cambios de tamaño, que a su vez pueden desencadenar un redibujado.
setNeedsDisplay(_:) debe usarse para cambios puntuales: movimiento del cursor, cambio de color de un área seleccionada, actualización de parte de un gráfico. Esto limita el área de redibujado y mejora el rendimiento en comparación con redibujar toda la vista.
Por defecto, cambiar el frame o bounds no llama a setNeedsDisplay(). El sistema solo mueve la vista. Si se requiere redibujado al cambiar el tamaño, establezca la propiedad contentMode en .redraw — entonces UIKit llamará automáticamente a setNeedsDisplay() en cada cambio de bounds.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también