draw(_:) / drawRect es un método de la clase UIView (en Swift) y su equivalente en Objective-C (drawRect:), que se encarga de renderizar el contenido de la vista usando Core Graphics. El sistema llama a este método automáticamente cuando la vista aparece por primera vez en pantalla y después de llamar a setNeedsDisplay(). Según la Documentación de Apple (2025), draw(_:) es el único método donde el desarrollador tiene acceso al contexto gráfico de la pantalla actual para dibujo personalizado. Sobrescribir draw(_:) otorga control total sobre la apariencia del componente, desde formas geométricas simples hasta gráficos animados complejos.
Puntos clave
draw(_:) es un método de instancia de UIView que UIKit llama para renderizar el contenido de la vista. Dentro de este método, el desarrollador obtiene acceso al contexto gráfico CGContext y utiliza la API de Core Graphics para dibujar líneas, rellenos, texto e imágenes. drawRect: en Objective-C realiza la misma función pero con sintaxis diferente: el único parámetro es un CGRect que especifica el área a redibujar.
Según Apple Engineering (2024), draw(_:) funciona mediante renderizado por CPU en un Bitmap Graphics Context, lo que proporciona máxima flexibilidad pero requiere más recursos en comparación con CALayer. La decisión de usar draw(_:) se toma según la complejidad de los gráficos y los requisitos de rendimiento.
En Swift, el método se declara como override func draw(_ rect: CGRect), donde rect es el rectángulo que necesita ser redibujado. En Objective-C, la firma es - (void)drawRect:(CGRect)rect. El parámetro rect puede ser menor que los bounds de la vista durante el redibujado parcial mediante setNeedsDisplay(_:).
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
El sistema llama automáticamente a draw(_:) en escenarios estrictamente definidos. Comprender estos desencadenantes ayuda a evitar redibujados innecesarios y optimizar el rendimiento de la vista. A continuación se enumeran todos los casos de invocación automática del método.
La documentación de Apple advierte: nunca llames a draw(_:) directamente. El sistema decide cuándo realizar el dibujo, y una llamada directa altera el mecanismo interno de caché. Para solicitar un redibujado, usa siempre setNeedsDisplay() o setNeedsDisplay(_:).
Sobrescribir draw(_:) en Swift comienza con la obtención del contexto gráfico y las llamadas posteriores a Core Graphics. Se recomienda crear métodos separados para bloques lógicos de dibujo — esto mejora la legibilidad y capacidad de prueba del código.
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
// Line parameters
context.setStrokeColor(UIColor.darkGray.cgColor)
context.setLineWidth(2.0)
// Drawing a triangle
context.move(to: CGPoint(x: rect.midX, y: rect.minY + 10))
context.addLine(to: CGPoint(x: rect.maxX - 10, y: rect.maxY - 10))
context.addLine(to: CGPoint(x: rect.minX + 10, y: rect.maxY - 10))
context.closePath()
context.strokePath()
}
En este ejemplo, draw(_:) dibuja un triángulo con un contorno gris oscuro de 2 píxeles de grosor. Llamar a super.draw(rect) al inicio del método es recomendado por Apple para preservar la lógica de dibujo padre, aunque la implementación predeterminada de draw(_:) en UIView está vacía.
Cada llamada a draw(_:) debe producir un resultado idéntico con los mismos datos de entrada. Esto permite que el sistema almacene en caché el resultado y no vuelva a llamar a draw(_:) si el contenido de la vista no ha cambiado. No uses valores aleatorios, hora del sistema ni solicitudes de red dentro de draw(_:).
drawRect: es históricamente la primera versión del método, aparecida en iOS 2.0 con Objective-C. En Swift, el método fue renombrado a draw(_:) usando el parámetro externo _. Funcionalmente, los métodos son idénticos: ambos reciben un CGRect del área a redibujar y usan UIGraphicsGetCurrentContext() para acceder al contexto gráfico.
| Característica | draw(_:) (Swift) | drawRect: (Objective-C) |
|---|---|---|
| Firma | override func draw(_ rect: CGRect) | - (void)drawRect:(CGRect)rect |
| Llamada a super | super.draw(rect) | [super drawRect:rect] |
| Contexto | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| Parámetro rect | rect: CGRect | CGRect rect |
| Rendimiento | Idéntico | Idéntico |
Al migrar un proyecto de Objective-C a Swift, renombrar el método es una de las primeras tareas. Xcode proporciona un convertidor automático, pero drawRect: requiere actualización manual a draw(_:). Según Apple (2024), la versión Swift draw(_:) es preferible para proyectos nuevos.
draw(_:) se ejecuta en la CPU, y una implementación subóptima puede causar caídas de fotogramas y bajo rendimiento. Apple recomienda varios enfoques probados para acelerar el dibujo.
Cada operación de Core Graphics (move(to:), addLine(to:), strokePath) tiene sobrecarga. Agrupa las operaciones y usa CGPath para formas complejas — la ruta se crea una vez y se reutiliza en cada llamada a draw(_:).
UIBezierPath es una envoltura de Objective-C sobre CGPath que proporciona una API simple para crear formas. Crea UIBezierPath con antelación (por ejemplo, en el inicializador) y simplemente llama a fill() o stroke() dentro de draw(_:).
private let starPath: UIBezierPath = {
let path = UIBezierPath()
// Building a star shape
path.move(to: CGPoint(x: 50, y: 0))
for i in 1...5 {
let angle = CGFloat(i) * 4 * CGFloat.pi / 5
path.addLine(to: CGPoint(x: 50 + 40 * cos(angle),
y: 50 + 40 * sin(angle)))
}
path.close()
return path
}()
override func draw(_ rect: CGRect) {
UIColor.systemYellow.setFill()
starPath.fill()
}
Los desarrolladores suelen cometer los mismos errores al sobrescribir draw(_:). Conocer estos patrones ayuda a evitar fallos y degradación del rendimiento. Veamos los problemas más comunes y sus soluciones.
Siguiendo estas reglas, asegurarás un dibujo personalizado estable y rápido en cualquier proyecto de iOS. Perfila draw(_:) mediante Instruments (Core Animation) para ver el tiempo de ejecución real y los cuellos de botella.
Preguntas frecuentes
No, la llamada directa a draw(_:) está prohibida por la documentación de Apple. El sistema gestiona el ciclo de dibujo por sí mismo. Para solicitar un redibujado, usa setNeedsDisplay(), que marca correctamente la vista como necesitada de actualización en el ciclo de renderizado más cercano.
Funcionalmente estos métodos son idénticos. drawRect: se usa en Objective-C, draw(_:) en Swift. Ambos reciben un CGRect del área a redibujar y usan el mismo contexto de Core Graphics mediante UIGraphicsGetCurrentContext().
Apple optimiza el renderizado: si un UIView no tiene draw(_:) sobrescrito, el sistema no crea un contexto bitmap para él. Esto ahorra memoria. Si la sobrescritura existe pero el método no se llama — comprueba que el frame de la vista no sea cero y que la vista sea visible.
Solo cuando es necesario: primer renderizado, después de setNeedsDisplay(), al cambiar los bounds con contentMode = .redraw. En estado estático, draw(_:) no se vuelve a llamar, ahorrando recursos de CPU y batería.
Apple recomienda llamar a super.draw(rect) al inicio del método sobrescrito. Aunque la implementación actual de UIView está vacía, la llamada a super asegura compatibilidad con futuras versiones de UIKit y es una buena práctica.
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