draw(_:) / drawRect is a method of the UIView class (in Swift) and its counterpart in Objective-C (drawRect:), which is responsible for rendering the view's content using Core Graphics. The system calls this method automatically when the view first appears on screen and after calling setNeedsDisplay(). According to Apple Documentation (2025), draw(_:) is the only method where the developer has access to the current screen's graphics context for custom drawing. Overriding draw(_:) gives full control over the component's appearance — from simple geometric shapes to complex animated graphics.
Key Takeaways
draw(_:) is an instance method of UIView that UIKit calls to render the view's content. Inside this method, the developer gets access to the CGContext graphics context and uses the Core Graphics API to draw lines, fills, text, and images. drawRect: in Objective-C performs the same function but with different syntax: the only parameter is a CGRect specifying the area to redraw.
According to Apple Engineering (2024), draw(_:) works through CPU-based Bitmap Graphics Context rendering, which provides maximum flexibility but requires more resources compared to CALayer. The decision to use draw(_:) is made based on the complexity of the graphics and performance requirements.
In Swift, the method is declared as override func draw(_ rect: CGRect), where rect is the rectangle that needs to be redrawn. In Objective-C, the signature is - (void)drawRect:(CGRect)rect. The rect parameter may be smaller than the view's bounds during partial redrawing via setNeedsDisplay(_:).
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
The system automatically calls draw(_:) in strictly defined scenarios. Understanding these triggers helps avoid unnecessary redraws and optimize view performance. Below are all cases of automatic method invocation.
Apple documentation warns: never call draw(_:) directly. The system itself decides when to perform drawing, and a direct call disrupts the internal caching mechanism. To request a redraw, always use setNeedsDisplay() or setNeedsDisplay(_:).
Overriding draw(_:) in Swift starts with obtaining the graphics context and subsequent calls to Core Graphics. It is recommended to create separate methods for logical drawing blocks — this improves code readability and testability.
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()
}
In this example, draw(_:) draws a triangle with a dark gray outline 2 pixels thick. Calling super.draw(rect) at the beginning of the method is recommended by Apple to preserve the parent drawing logic, although UIView's default draw(_:) implementation is empty.
Each call to draw(_:) should produce an identical result given the same input data. This allows the system to cache the result and not call draw(_:) again if the view's content hasn't changed. Do not use random values, system time, or network requests inside draw(_:).
drawRect: is historically the first version of the method, appearing in iOS 2.0 with Objective-C. In Swift, the method was renamed to draw(_:) using the external parameter _. Functionally, the methods are identical: both receive a CGRect of the area to redraw and use UIGraphicsGetCurrentContext() to access the graphics context.
| Characteristic | draw(_:) (Swift) | drawRect: (Objective-C) |
|---|---|---|
| Signature | override func draw(_ rect: CGRect) | - (void)drawRect:(CGRect)rect |
| Calling super | super.draw(rect) | [super drawRect:rect] |
| Context | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| rect parameter | rect: CGRect | CGRect rect |
| Performance | Identical | Identical |
When migrating a project from Objective-C to Swift, renaming the method is one of the first tasks. Xcode provides an automatic converter, but drawRect: requires manual updating to draw(_:). According to Apple (2024), the Swift version draw(_:) is preferred for new projects.
draw(_:) runs on the CPU, and a suboptimal implementation can cause frame drops and poor performance. Apple recommends several proven approaches to speed up drawing.
Each Core Graphics operation (move(to:), addLine(to:), strokePath) has overhead. Group operations and use CGPath for complex shapes — the path is created once and reused on every draw(_:) call.
UIBezierPath is an Objective-C wrapper over CGPath that provides a simple API for creating shapes. Create UIBezierPath in advance (e.g., in the initializer) and simply call fill() or stroke() inside 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()
}
Developers often make the same mistakes when overriding draw(_:). Knowing these patterns helps avoid bugs and performance degradation. Let's look at the most common problems and their solutions.
By following these rules, you will ensure stable and fast custom view drawing in any iOS project. Profile draw(_:) through Instruments (Core Animation) to see actual execution time and bottlenecks.
Frequently Asked Questions
No, directly calling draw(_:) is prohibited by Apple documentation. The system manages the drawing cycle itself. To request a redraw, use setNeedsDisplay(), which correctly marks the view as needing an update in the nearest rendering cycle.
Functionally these methods are identical. drawRect: is used in Objective-C, draw(_:) in Swift. Both receive a CGRect of the area to redraw and use the same Core Graphics context via UIGraphicsGetCurrentContext().
Apple optimizes rendering: if a UIView has no overridden draw(_:), the system does not create a bitmap context for it. This saves memory. If the override exists but the method is not called — check that the view's frame is non-zero and the view is visible.
Only when needed: first render, after setNeedsDisplay(), when bounds change with contentMode = .redraw. In a static state, draw(_:) is not called again, saving CPU and battery resources.
Apple recommends calling super.draw(rect) at the beginning of the overridden method. Although the current UIView implementation is empty, the super call ensures compatibility with future UIKit versions and is good practice.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also