CircleProgressView
DisplayIBDesignable circular progress ring drawn with CoreGraphics UIBezierPath. Uses CADisplayLink for smooth animated progress updates. Supports clockwise/counter-clockwise direction, rounded arc caps, track image overlay, custom center color/image, and a configurable border.
🎨 Key Properties
| Property | Type | Default | Description |
|---|---|---|---|
progress | Double | 0.0 | Progress value 0.0 → 1.0 |
trackWidth | CGFloat | 10 | Ring stroke width |
trackFillColor | UIColor | .blue | Progress arc fill color |
trackBackgroundColor | UIColor | .gray | Track (remaining) ring color |
centerFillColor | UIColor | .white | Inner circle fill color |
centerImage | UIImage? | nil | Image clipped inside inner circle |
trackImage | UIImage? | nil | Image used as progress arc fill (replaces trackFillColor) |
trackBorderColor | UIColor | .clear | Outer border stroke color |
trackBorderWidth | CGFloat | 0 | Outer border width |
roundedCap | Bool | false | Rounded arc endpoints |
clockwise | Bool | true | Arc direction |
refreshRate | Double | 0.0 | Custom CADisplayLink tick rate (0 = device display rate) |
contentView | UIView | Container for center subviews (add percentage label etc.) |
🚀 Usage
Basic Circular Progress
let ringView = CircleProgressView(frame: CGRect(x: 0, y: 0, width: 120, height: 120))
ringView.trackWidth = 12
ringView.trackFillColor = .systemBlue
ringView.trackBackgroundColor = .systemGray5
ringView.centerFillColor = .white
// Set without animation
ringView.progress = 0.75
// Set with smooth CADisplayLink animation
ringView.setProgress(0.75, animated: true)
Percentage Label in Center
let ringView = CircleProgressView(frame: CGRect(x: 0, y: 0, width: 120, height: 120))
ringView.trackFillColor = .systemGreen
ringView.trackWidth = 10
ringView.centerFillColor = .white
// Add label to the center contentView
let percentLabel = UILabel()
percentLabel.text = "75%"
percentLabel.textAlignment = .center
percentLabel.font = .boldSystemFont(ofSize: 22)
percentLabel.textColor = .systemGreen
percentLabel.translatesAutoresizingMaskIntoConstraints = false
ringView.contentView.addSubview(percentLabel)
// Center it
percentLabel.centerXAnchor.constraint(equalTo: ringView.contentView.centerXAnchor).isActive = true
percentLabel.centerYAnchor.constraint(equalTo: ringView.contentView.centerYAnchor).isActive = true
ringView.setProgress(0.75, animated: true)
Animated Fill (Download/Upload Progress)
let downloadRing = CircleProgressView(frame: .zero)
downloadRing.trackWidth = 8
downloadRing.trackFillColor = .systemBlue
downloadRing.roundedCap = true
downloadRing.setProgress(0, animated: false)
// Update as download progresses
downloadTask.progressHandler = { progress in
DispatchQueue.main.async {
downloadRing.setProgress(progress, animated: true)
}
}
With Image in Center
let avatarRing = CircleProgressView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
avatarRing.trackWidth = 4
avatarRing.trackFillColor = .systemOrange
avatarRing.centerImage = UIImage(named: "avatar")
avatarRing.setProgress(0.6, animated: true)
📖 API
| Method | Description |
|---|---|
setProgress(_:animated:) | Set progress with optional CADisplayLink animation |
progress | Direct set (no animation) |
Pro Tip: Add
UILabel or other views to contentView — it's automatically masked to the inner circle shape so your content is clipped correctly. Use roundedCap = true for a more polished look on thin rings.