Custom Rendering with draw(_:)
draw(_:) is the callback where UIKit asks your view for pixels. Override it to render custom shapes or text with Core Graphics when stock views fall short. The footgun is calling it directly or doing non-drawing work inside; use setNeedsDisplay() instead.
WHY IT EXISTS: UIKit provides standard views like UILabel and UIImageView, but sometimes you need a bespoke visual that compositing layers cannot express efficiently. Custom drawing lets you generate pixels directly rather than assembling a heavy subview hierarchy. draw(_:) exists as the single entry point where the framework hands you a Core Graphics context and asks you to paint.
THE MENTAL MODEL: Think of draw(_:) like a private chef being called to the kitchen. You do not tell the chef to cook whenever you feel like it; the restaurant manager decides when a customer orders. Your job is to have the recipe ready. When UIKit calls draw(_:), it is the order ticket. You look at the rect, pick up the current graphics context, and execute your drawing commands. If you try to call the chef directly, you bypass the kitchen schedule and likely find no ingredients prepared.
HOW IT WORKS: UIKit maintains a render tree. When a view is marked dirty via setNeedsDisplay or setNeedsDisplay(_:), the system schedules a pass on the main run loop. At the appropriate time, UIKit creates or updates a CGContext for the view, clips it to the provided CGRect, pushes it as the current context, and invokes your draw(_:) override. Inside, you use UIKit methods like UIBezierPath.stroke() or Core Graphics functions like CGContextAddPath to paint. Once you return, UIKit captures the output and composites it. The rect parameter is the dirty region, not the entire bounds, so drawing outside it wastes GPU fill rate.
WHEN TO USE IT: Use draw(_:) for static or slowly changing vector art, chart plots, waveform visualizations, or text layouts that need pixel-perfect control. It shines when a single view can replace dozens of layered subviews and reduce memory pressure from backing stores. It is also the right place for real-time Core Graphics effects that do not map well to CALayer properties.
WHEN NOT TO USE IT: Do not use it for animations that can be driven by UIView.animate or Core Animation layer properties; those run on the render server and avoid CPU pixel generation. Avoid it if the content changes every frame at sixty hertz, because CPU drawing and texture upload can bottleneck the GPU. Never perform model updates, network requests, or view layout inside draw(_:); it is a pure function of your model state and should run fast.
ONE CANONICAL EXAMPLE: Imagine a custom progress ring. You subclass UIView, override draw(_:), and use UIBezierPath to stroke an arc whose end angle derives from a progress property. When progress changes, you call setNeedsDisplay(). UIKit then invokes draw(_:) with the view bounds, you set the stroke color and line width, and draw the arc into the current context. The result is a crisp ring with no extra image assets or subviews.
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.