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.
Interview question
In a custom UIView subclass that draws a progress ring, you update the progress property. What is the correct way to trigger a redraw?
- a.Call draw(_:) directly with the view's bounds to force an immediate redraw
- b.Call setNeedsDisplay() and let UIKit invoke draw(_:) with a prepared contextCorrect
- c.Assign a new CALayer contents image inside the progress setter
- d.Call layoutIfNeeded() to trigger the draw(_:) override automatically
Why? this is the answer
setNeedsDisplay() marks the view dirty so UIKit schedules a pass and invokes draw(_:) with a valid Core Graphics context already prepared. Calling draw(_:) directly bypasses this setup and risks missing or invalid context state, which is why the card labels it a footgun.
Just read this? Test yourself on what you have been reading.
- #ios
- #swift
- #uikit
- #core-graphics
- #rendering
Put your scrolling time to good use
Learn one idea, try a quiz and save useful cards for revision. Tezvyn makes it easy to learn and stay current in your tech field, a few minutes at a time.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on ios — each one lists the topics its interview covers.
See open roles