draw(_:) y drawRect — qué son, llamada y sobrescritura

Autor: IT Sectr Publicado: 2026-07-20 Tiempo de lectura: 7 min

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(_:) — método de UIView para dibujo personalizado mediante Core Graphics, no se llama directamente por el desarrollador
  • drawRect: — equivalente de draw(_:) en Objective-C, sintácticamente diferente, funcionalmente idéntico
  • CGContext — contexto gráfico disponible dentro de draw(_:) para todas las operaciones de dibujo
  • setNeedsDisplay() — la única forma correcta de solicitar un redibujado de draw(_:) desde el lado del desarrollador
  • UIGraphicsGetCurrentContext() — función para obtener el contexto actual dentro de draw(_:), paso obligatorio antes de dibujar

Qué es draw(_:) / drawRect

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.

Firma del método en Swift y Objective-C

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(_:).

objective-c
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillRect(context, rect);
}

Cuándo el sistema llama a draw(_:)

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.

  • Primer renderizado — cuando la vista se agrega por primera vez a la jerarquía y se vuelve visible en pantalla
  • setNeedsDisplay() — después de llamar a este método, el sistema redibuja durante el próximo ciclo de dibujo
  • setNeedsDisplay(_:) — igual que el anterior, pero especificando un rectángulo concreto para redibujar
  • contentMode — cuando cambian los bounds, si contentMode requiere redibujado (por ejemplo, .redraw)
  • setNeedsLayout() — en algunos casos, después de reorganizar las subvistas, puede ser necesario redibujar

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(_:).

Cómo sobrescribir draw(_:) en Swift

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.

swift
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.

La regla de idempotencia de draw(_:)

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: en Objective-C y diferencias con 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ísticadraw(_:) (Swift)drawRect: (Objective-C)
Firmaoverride func draw(_ rect: CGRect)- (void)drawRect:(CGRect)rect
Llamada a supersuper.draw(rect)[super drawRect:rect]
ContextoUIGraphicsGetCurrentContext()UIGraphicsGetCurrentContext()
Parámetro rectrect: CGRectCGRect rect
RendimientoIdénticoIdé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.

Optimización del dibujo en draw(_:)

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.

Minimiza el número de operaciones de 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(_:).

Usa UIBezierPath para objetos vectoriales

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(_:).

swift
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()
}

Errores comunes al trabajar con draw(_:)

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.

  • Llamada directa a draw(_:) — nunca llames a draw(_:) directamente. Usa setNeedsDisplay() para solicitar un redibujado. La llamada directa rompe la caché y puede provocar una visualización incorrecta.
  • Cálculos pesados dentro de draw(_:) — draw(_:) debe ser lo más ligero posible. Crea UIBezierPath, imágenes y otros objetos pesados fuera del método, inicializándolos una sola vez.
  • Creación de objetos dentro de draw(_:) — las construcciones de UIColor, UIFont y UIGraphicsImageRenderer dentro de draw(_:) crean carga innecesaria de memoria. Traslada la creación de objetos a propiedades de la clase.
  • Ignorar el parámetro rect — rect especifica el área que necesita redibujado. Dibujar fuera de rect es descartado por el sistema pero consume recursos. Comprueba la intersección con rect antes de dibujar.
  • Falta de super.draw(rect) — aunque la implementación de UIView está vacía, Apple recomienda llamar a super.draw(rect) para compatibilidad con futuros cambios de UIKit.

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

¿Se puede llamar a draw(_:) directamente?

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.

¿En qué se diferencia drawRect: de draw(_:)?

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().

¿Por qué no se llama a draw(_:) para un UIView vacío?

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.

¿Con qué frecuencia el sistema llama a draw(_:)?

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.

¿Es necesario llamar a super.draw(rect) en Swift?

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

  • draw(_:) / drawRect — el método principal de dibujo de UIView en Swift y Objective-C, llamado automáticamente por el sistema
  • CGContext — contexto de Core Graphics disponible dentro de draw(_:) mediante UIGraphicsGetCurrentContext()
  • setNeedsDisplay() — la forma correcta de solicitar un redibujado; la llamada directa a draw(_:) está prohibida
  • Idempotencia — draw(_:) debe producir el mismo resultado con los mismos datos de entrada para un caché adecuado
  • UIBezierPath — crea rutas fuera de draw(_:) para minimizar la carga de CPU en cada llamada
  • Parámetro rect — contiene el área de redibujado; usa intersección para optimizar — no dibujes fuera de él
  • Perfilado — verifica el rendimiento de draw(_:) mediante Instruments Core Animation para identificar operaciones lentas

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.

Discutir el proyecto

Lea también