iOS Development
Custom UIView
iPhone App Design
Circle Drawing
Swift Programming

How to draw a custom UIView that is just a circle - iPhone app

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Drawing a circular view in iOS seems simple, but correct implementation depends on layout timing, constraint setup, and rendering strategy. Many bugs happen when radius is set before the final size is known, or when width and height are allowed to drift apart. A good solution starts with the simplest rendering technique that matches the actual design need.

Choose the Right Technique for the Job

There are three common ways to build a circle in UIKit:

  • Layer corner radius for simple filled circles.
  • CAShapeLayer for ring outlines and stroke animations.
  • draw for custom paint behavior.

Use the simplest technique that covers your requirements. Overengineering a static dot with custom drawing adds maintenance cost without adding any user-facing value.

Method 1: Corner Radius in layoutSubviews

For plain circles, set corner radius from current bounds inside layoutSubviews.

swift
1import UIKit
2
3final class CircleView: UIView {
4    override func layoutSubviews() {
5        super.layoutSubviews()
6        layer.cornerRadius = min(bounds.width, bounds.height) / 2
7        layer.masksToBounds = true
8    }
9}

Why this works:

  • layoutSubviews runs after Auto Layout calculates final size.
  • Radius updates correctly on rotation or constraint changes.
  • GPU handles this path efficiently for most UI components.

Enforce Square Geometry with Constraints

A true circle requires equal width and height. Add explicit constraints to preserve shape.

swift
1let avatarDot = CircleView()
2avatarDot.backgroundColor = .systemGreen
3avatarDot.translatesAutoresizingMaskIntoConstraints = false
4
5view.addSubview(avatarDot)
6NSLayoutConstraint.activate([
7    avatarDot.widthAnchor.constraint(equalToConstant: 48),
8    avatarDot.heightAnchor.constraint(equalTo: avatarDot.widthAnchor),
9    avatarDot.centerXAnchor.constraint(equalTo: view.centerXAnchor),
10    avatarDot.centerYAnchor.constraint(equalTo: view.centerYAnchor)
11])

Without the equal-size rule, the same view can become an ellipse on some layouts.

Method 2: CAShapeLayer for Ring and Border Effects

When you need a ring, progress arc, or animated stroke, CAShapeLayer gives better control.

swift
1import UIKit
2
3final class RingView: UIView {
4    private let ringLayer = CAShapeLayer()
5
6    override init(frame: CGRect) {
7        super.init(frame: frame)
8        layer.addSublayer(ringLayer)
9        ringLayer.fillColor = UIColor.clear.cgColor
10        ringLayer.strokeColor = UIColor.systemBlue.cgColor
11        ringLayer.lineWidth = 6
12    }
13
14    required init?(coder: NSCoder) {
15        fatalError("init(coder:) has not been implemented")
16    }
17
18    override func layoutSubviews() {
19        super.layoutSubviews()
20        let inset = ringLayer.lineWidth / 2
21        let ringRect = bounds.insetBy(dx: inset, dy: inset)
22        ringLayer.path = UIBezierPath(ovalIn: ringRect).cgPath
23    }
24}

This method avoids manual pixel math and supports smooth animation.

Method 3: Custom Paint in draw

Use draw when you need full visual control, such as gradients, multiple strokes, or dynamic paint logic.

swift
1import UIKit
2
3final class PaintedCircleView: UIView {
4    override func draw(_ rect: CGRect) {
5        guard let ctx = UIGraphicsGetCurrentContext() else { return }
6
7        let side = min(rect.width, rect.height)
8        let x = (rect.width - side) / 2
9        let y = (rect.height - side) / 2
10        let circleRect = CGRect(x: x, y: y, width: side, height: side)
11
12        ctx.setFillColor(UIColor.systemTeal.cgColor)
13        ctx.fillEllipse(in: circleRect)
14
15        ctx.setStrokeColor(UIColor.white.cgColor)
16        ctx.setLineWidth(2)
17        ctx.strokeEllipse(in: circleRect.insetBy(dx: 1, dy: 1))
18    }
19}

Keep draw lightweight. Heavy computation there can hurt scrolling and animation performance.

Add Animation Safely

A simple rotation animation can make a circular indicator feel responsive.

swift
1import QuartzCore
2
3func startSpin(on view: UIView) {
4    let spin = CABasicAnimation(keyPath: "transform.rotation")
5    spin.fromValue = 0
6    spin.toValue = Double.pi * 2
7    spin.duration = 1.0
8    spin.repeatCount = .infinity
9    view.layer.add(spin, forKey: "spin")
10}

If motion is decorative, respect reduced motion settings in accessibility preferences.

Reusability and Accessibility

For reusable components, expose simple configuration points such as fill color and border width. Also set accessibility behavior intentionally.

swift
circleView.isAccessibilityElement = true
circleView.accessibilityLabel = "Connection status"
circleView.accessibilityTraits = [.image]

If the circle is purely decorative, set it as not accessible to avoid noise for assistive technologies.

Common Pitfalls

  • Setting corner radius in init before bounds are final.
  • Forgetting equal width and height constraints.
  • Doing expensive operations repeatedly in draw.
  • Animating many circle layers without checking performance impact.
  • Ignoring accessibility roles for circles that convey important state.

Summary

  • Use corner radius for the simplest and most performant circle view.
  • Use CAShapeLayer when ring styling or stroke animation is required.
  • Use custom draw only for visuals that cannot be achieved with layers.
  • Enforce square constraints to guarantee a true circle shape.
  • Treat accessibility and motion behavior as part of component design.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions