iOS Development
Swift Programming
Drawing in Swift
UIKit
Circle Drawing

How do I draw a circle in iOS Swift?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In UIKit, drawing a circle is not about a special circle API. It is about choosing the right drawing surface. Most of the time, CAShapeLayer is the cleanest solution because it is easy to style, resize, and animate, while custom drawing in draw(_:) is better when the circle is just one element in a larger rendered view.

Use CAShapeLayer for Most UI Circles

A circle is simply an oval inside a square rectangle. With CAShapeLayer, you describe that path once and let Core Animation handle the rendering:

swift
1import UIKit
2
3final class CircleView: UIView {
4    private let shapeLayer = CAShapeLayer()
5
6    override init(frame: CGRect) {
7        super.init(frame: frame)
8        configure()
9    }
10
11    required init?(coder: NSCoder) {
12        super.init(coder: coder)
13        configure()
14    }
15
16    private func configure() {
17        shapeLayer.fillColor = UIColor.clear.cgColor
18        shapeLayer.strokeColor = UIColor.systemBlue.cgColor
19        shapeLayer.lineWidth = 4
20        layer.addSublayer(shapeLayer)
21    }
22
23    override func layoutSubviews() {
24        super.layoutSubviews()
25        let insetRect = bounds.insetBy(dx: 4, dy: 4)
26        shapeLayer.path = UIBezierPath(ovalIn: insetRect).cgPath
27    }
28}

This approach is flexible. You can change stroke color, fill color, dash style, and animation properties without rewriting drawing code. Updating the path in layoutSubviews also ensures the circle stays correct when Auto Layout changes the view size.

Fill the Circle or Draw a Ring

To draw a solid disk, give the layer a fill color and clear the stroke:

swift
shapeLayer.fillColor = UIColor.systemRed.cgColor
shapeLayer.strokeColor = UIColor.clear.cgColor

To draw a ring, keep the fill transparent and set lineWidth plus strokeColor. That covers common use cases such as badges, avatar outlines, and progress visuals.

The key design choice is whether the circle is a standalone shape layer or the entire view itself. If the whole view should be circular, a rounded layer may be even simpler.

When cornerRadius Is Enough

If the goal is just to make a view appear circular, do not over-engineer it. A square view with half-width corner radius becomes a circle:

swift
avatarView.layer.cornerRadius = avatarView.bounds.width / 2
avatarView.clipsToBounds = true

This is perfect for image views, colored dots, or simple profile placeholders. It does not draw a separate vector path, but for many interface elements that is exactly what you want.

Use draw(_:) for Custom Rendering

If your view already draws multiple shapes, labels, or custom graphics, writing directly in draw(_:) can be the right choice:

swift
1import UIKit
2
3final class CustomCircleView: UIView {
4    override func draw(_ rect: CGRect) {
5        UIColor.systemGreen.setStroke()
6        UIColor.clear.setFill()
7
8        let path = UIBezierPath(ovalIn: rect.insetBy(dx: 6, dy: 6))
9        path.lineWidth = 4
10        path.fill()
11        path.stroke()
12    }
13}

This keeps rendering logic in one place, but it is more work to maintain than a shape layer. Use it when custom drawing is the point of the view, not when you only need one circle.

Make Sure the Drawing Area Is Square

One easy mistake is forgetting that ovalIn: draws the largest oval that fits the rectangle. If the rectangle is wider than it is tall, you get an ellipse, not a circle.

When the containing view is not square, compute a centered square drawing region:

swift
1let side = min(bounds.width, bounds.height)
2let rect = CGRect(
3    x: (bounds.width - side) / 2,
4    y: (bounds.height - side) / 2,
5    width: side,
6    height: side
7)
8let path = UIBezierPath(ovalIn: rect)

That guarantees the geometry stays circular regardless of the view's aspect ratio.

Common Pitfalls

  • Drawing into a non-square rectangle and accidentally producing an ellipse.
  • Creating the path once in init even though the final view size is determined later by Auto Layout.
  • Reaching for draw(_:) when a simple CAShapeLayer would be easier to style and animate.
  • Forgetting to set either a fill color or a stroke color, which can make the shape invisible.
  • Using cornerRadius without clipsToBounds when the view content should also be clipped.

Summary

  • In UIKit, circles are usually drawn with UIBezierPath(ovalIn:).
  • 'CAShapeLayer is the best default for most stroked or filled circle UI elements.'
  • 'cornerRadius is the simplest option when the entire view should appear circular.'
  • Use draw(_:) when the circle is part of broader custom rendering.
  • Always use a square drawing region if you need a true circle instead of an ellipse.

Course illustration
Course illustration

All Rights Reserved.