CircleProgressView

Display

IBDesignable 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

PropertyTypeDefaultDescription
progressDouble0.0Progress value 0.0 → 1.0
trackWidthCGFloat10Ring stroke width
trackFillColorUIColor.blueProgress arc fill color
trackBackgroundColorUIColor.grayTrack (remaining) ring color
centerFillColorUIColor.whiteInner circle fill color
centerImageUIImage?nilImage clipped inside inner circle
trackImageUIImage?nilImage used as progress arc fill (replaces trackFillColor)
trackBorderColorUIColor.clearOuter border stroke color
trackBorderWidthCGFloat0Outer border width
roundedCapBoolfalseRounded arc endpoints
clockwiseBooltrueArc direction
refreshRateDouble0.0Custom CADisplayLink tick rate (0 = device display rate)
contentViewUIViewContainer 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

MethodDescription
setProgress(_:animated:)Set progress with optional CADisplayLink animation
progressDirect 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.