Skip to content

Instantly share code, notes, and snippets.

@zafarivaev
Last active June 1, 2023 12:29
Show Gist options
  • Save zafarivaev/530c205aacb564b649bcb41ad3063112 to your computer and use it in GitHub Desktop.
Save zafarivaev/530c205aacb564b649bcb41ad3063112 to your computer and use it in GitHub Desktop.
import UIKit
@IBDesignable class CircularProgressView: UIView {
// MARK: - Initialization
public override init(frame: CGRect) {
super.init(frame: frame)
}
required public init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Segments
struct Segment: Comparable {
let ratio: CGFloat
let color: UIColor
static func < (
lhs: CircularProgressView.Segment,
rhs: CircularProgressView.Segment
) -> Bool {
return lhs.ratio < rhs.ratio
}
}
open var segments: [Segment] = [
.init(ratio: 0.25, color: .darkGray),
.init(ratio: 0.4, color: .systemYellow),
.init(ratio: 0.35, color: .systemBlue),
] {
didSet {
self.setNeedsDisplay()
}
}
// MARK: - Drawing
override open func draw(_ rect: CGRect) {
drawSegments(segments: segments)
}
// MARK: - Drawing
private func drawSegments(segments: [Segment]) {
let center = CGPoint(
x: bounds.width / 2,
y: bounds.height / 2
)
let radius = max(
bounds.width / 2 - 10,
bounds.height / 2 - 10
)
let sortedSegments = segments.sorted(by: >)
var startAngle: CGFloat = (3 * .pi) / 2
sortedSegments.enumerated().forEach { index, segment in
let endAngle = (startAngle + (((segment.ratio * 360) * .pi) / 180))
let visibleStartAngle = startAngle * 1.005
let visibleEndAngle = endAngle * 0.995
drawSegment(
center: center,
radius: radius,
startAngle: visibleStartAngle,
endAngle: visibleEndAngle,
color: segment.color
)
startAngle = endAngle
}
}
private func drawSegment(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, color: UIColor) {
let outerPath = UIBezierPath(
arcCenter: center,
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
clockwise: true
)
outerPath.lineWidth = outerThickness
outerPath.lineCapStyle = .round
color.setStroke()
outerPath.stroke()
}
// MARK: - Starting angles
public enum Angle {
case degrees(CGFloat)
case radian(CGFloat)
}
// MARK: - Thicknesses
@IBInspectable open var outerThickness: CGFloat = 10 {
didSet {
self.setNeedsDisplay()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment