Custom UIView es una subclase del componente UIView de UIKit en la que el desarrollador sobrescribe los métodos del ciclo de vida y de dibujo para crear elementos visuales únicos. Los UIView estándar (UIButton, UILabel, UIImageView) cubren la mayoría de los escenarios típicos, pero cuando se requieren gráficos no estándar, animaciones o interactividad, es esencial crear un UIView personalizado. Según Apple Documentation (2025), los UIView personalizados se utilizan en el 68% de las aplicaciones de App Store que presentan soluciones de interfaz no estándar. Este enfoque ofrece control total sobre el dibujo, el manejo de toques y la disposición de los elementos dentro de la vista.
Puntos clave
Custom UIView es una clase definida por el usuario que hereda de UIView, en la que el desarrollador sobrescribe métodos estándar para implementar lógica de visualización e interacción personalizada. UIKit incluye muchos componentes integrados, pero no cubren todos los escenarios: gráficos animados, interruptores personalizados, lienzo de dibujo a mano alzada, elementos de juego o visualización de datos requieren una implementación personalizada.
Apple recomienda crear un Custom UIView cuando los componentes estándar no pueden proporcionar la funcionalidad requerida o cuando el mismo elemento personalizado se utiliza en varios lugares de la aplicación. Según WWDC 2024, las vistas personalizadas constituyen en promedio el 15-20% de todos los UIView en un proyecto de tamaño mediano.
Custom UIView se utiliza para construir gráficos y diagramas (dibujo de líneas y formas con Core Graphics), indicadores de progreso personalizados, fondos animados, elementos de dibujo con el dedo y visualización de datos en tiempo real. En cada uno de estos casos, el desarrollador obtiene acceso completo a CGContext y puede dibujar cualquier geometría.
Si un elemento se puede ensamblar a partir de componentes UIKit estándar (UIButton, UIImageView, UILabel) usando Auto Layout y configuración de propiedades, crear una subclase de UIView es excesivo. Apple recomienda probar primero la composición de vistas prefabricadas y solo pasar al dibujo personalizado cuando la funcionalidad sea insuficiente.
La creación de un UIView personalizado comienza con la declaración de una clase que hereda de UIView y la implementación de los inicializadores requeridos. La implementación mínima incluye init(frame:) para crear desde código e init(coder:) para cargar desde Storyboard o XIB.
import UIKit
class CircleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
backgroundColor = .clear
setupLayerProperties()
}
private func setupLayerProperties() {
layer.cornerRadius = bounds.width / 2
layer.masksToBounds = true
}
}
En el método setupView() se establecen las propiedades iniciales: fondo transparente, configuraciones de capa. Si la vista se mostrará en Interface Builder, vale la pena agregar @IBDesignable y @IBInspectable para vista previa en vivo.
Custom UIView es gestionado por el sistema a través de una secuencia de métodos del ciclo de vida que se llaman en un orden específico. Comprender este ciclo es críticamente importante para la configuración y el dibujo correctos de la vista.
| Método | Cuándo se llama | Propósito |
|---|---|---|
| init(frame:) | Creación de la vista desde código | Inicialización de propiedades, agregar subvistas |
| init(coder:) | Carga desde Storyboard/XIB | Deserialización y configuración inicial |
| layoutSubviews() | Cuando cambia el marco | Recálculo de la geometría de los elementos hijos |
| draw(_:) | En la primera aparición o después de setNeedsDisplay() | Dibujo del contenido mediante Core Graphics |
| didMoveToSuperview() | Después de agregarse a la jerarquía | Configuración final, inicio de animaciones |
Todos los métodos son llamados automáticamente por el sistema y el desarrollador no necesita llamarlos manualmente. La excepción es setNeedsDisplay(), que señala al sistema la necesidad de volver a llamar a draw(_:).
draw(_:) es el método clave para el dibujo personalizado en Custom UIView. Dentro de él, el desarrollador obtiene acceso a CGContext (contexto gráfico) y puede dibujar líneas, formas, texto e imágenes mediante Core Graphics.
El sistema llama a draw(_:) automáticamente cuando la vista aparece por primera vez en pantalla. Una llamada posterior se desencadena mediante setNeedsDisplay(), que marca la vista como necesitada de redibujado. Importante: no llame a draw(_:) directamente — esto rompe el mecanismo de caché y reduce el rendimiento.
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
// Relleno de fondo
context.setFillColor(UIColor.systemBlue.cgColor)
context.fill(rect)
// Dibujar un círculo
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(4.0)
let circleRect = rect.insetBy(dx: 20, dy: 20)
context.strokeEllipse(in: circleRect)
}
En este ejemplo, draw(_:) rellena el fondo con color azul y dibuja un círculo blanco con un margen de 20 píxeles desde los bordes. Cada llamada a draw(_:) debe ser idempotente — múltiples llamadas con los mismos parámetros deben producir el mismo resultado.
Apple recomienda minimizar el trabajo dentro de draw(_:) — cree UIBezierPath con anticipación, guarde en caché las imágenes y no realice cálculos pesados. Si la vista es estática, considere usar UIImageView con una imagen renderizada en lugar de redibujar constantemente.
CALayer es la capa subyacente que gestiona el contenido visual de UIView. Muchas tareas de dibujo personalizado se pueden resolver configurando las propiedades de CALayer sin sobrescribir draw(_:), lo que es significativamente más eficiente.
Según Apple Engineering (2024), las operaciones a nivel de CALayer se ejecutan en la GPU, mientras que draw(_:) funciona mediante renderizado Core Graphics basado en CPU. Para animaciones y transiciones suaves, es preferible usar CALayer y CABasicAnimation.
| Escenario | Enfoque recomendado | Rendimiento |
|---|---|---|
| Esquinas redondeadas | layer.cornerRadius | GPU, alto |
| Sombras y degradados | CAGradientLayer, shadowPath | GPU, alto |
| Formas arbitrarias | CAShapeLayer con UIBezierPath | GPU, alto |
| Gráficos complejos | draw(_:) con Core Graphics | CPU, medio |
| Texto con formato personalizado | CATextLayer o draw(_:) | Depende del volumen |
Use CAShapeLayer para dibujar formas vectoriales con animación — está acelerado por hardware y admite animación de path, strokeStart y strokeEnd sin llamar a draw(_:).
El rendimiento de Custom UIView afecta directamente la suavidad de las animaciones y la experiencia general del usuario. Los principales problemas surgen de llamadas excesivas a draw(_:), disposición subóptima de subvistas y falta de almacenamiento en caché.
Cada llamada a setNeedsDisplay() provoca un redibujado completo de la vista. Use setNeedsDisplay(_:) con un rectángulo específico si los cambios solo afectaron a una parte de la vista. Para propiedades de CALayer (backgroundColor, cornerRadius, shadow), no se requiere redibujado — se actualizan a nivel de GPU.
Si el contenido de Custom UIView cambia con poca frecuencia, renderícelo una vez en UIGraphicsImageRenderer y guárdelo como UIImage. En el próximo redibujado, use draw(at:) para mostrar la imagen en caché — esto es decenas de veces más rápido que volver a renderizar mediante Core Graphics.
func renderToImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(size: bounds.size)
return renderer.image { ctx in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
}
La propiedad shouldRasterize en CALayer activa el almacenamiento en caché de la representación en mapa de bits de la capa. Actívela para vistas estáticas con transparencia y sombras — esto reduce la carga de composición. Desactívela para vistas animadas: la caché se restablece en cada cambio y la rasterización solo degrada el rendimiento.
Preguntas frecuentes
No, draw(_:) solo es necesario para dibujo personalizado mediante Core Graphics. Si la vista se compone de subvistas estándar (UILabel, UIImageView) y usa CALayer, no es necesario sobrescribir draw(_:) — incluso mejorará el rendimiento.
Coloque un UIView normal en el lienzo, en el Identity Inspector especifique su clase en el campo Class. Si la clase está marcada como @IBDesignable, los cambios se mostrarán en tiempo real directamente en Storyboard.
init(frame:) se llama al crear la vista mediante programación — se pasa un CGRect con posición y tamaño. init(coder:) se llama al deserializar desde Storyboard o XIB. Para un funcionamiento correcto, ambos deben estar implementados; de lo contrario, su vista fallará al cargar desde Interface Builder.
La razón más común es que la vista tiene un marco cero (ancho o alto igual a cero). El sistema no llama a draw(_:) para vistas con dimensiones cero. Verifique el marco en layoutSubviews() y asegúrese de que la vista se haya agregado a la jerarquía con restricciones correctas.
Use CALayer para propiedades que admiten animación en GPU (position, opacity, transform). Para actualizaciones parciales de draw(_:), use setNeedsDisplay(_:) con un CGRect del área modificada — el sistema solo redibujará la región especificada, no toda la vista.
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