Skip to content

Instantly share code, notes, and snippets.

@e-sung
Last active September 17, 2022 02:20
Show Gist options
  • Save e-sung/a57194c8d92c886339b324d9e8651d9b to your computer and use it in GitHub Desktop.
Save e-sung/a57194c8d92c886339b324d9e8651d9b to your computer and use it in GitHub Desktop.
ProgressBar that has circular form with gradient
@IBDesignable
public class CircularProgressView: UIView {
@IBInspectable public var progress: Float = 0 {
didSet {
drawProgress()
}
}
public var lineWidth: CGFloat = 4 {
didSet {
setNeedsLayout()
}
}
public var startColor: UIColor = .blue {
didSet {
setNeedsLayout()
}
}
public var endColor: UIColor = .controlBlue {
didSet {
setNeedsLayout()
}
}
var progressBarRadius: CGFloat {
return (width - 7) / 2
}
var backgroundRadius: CGFloat {
return progressBarRadius - lineWidth
}
var tipRadius: CGFloat {
return progressBarRadius - (lineWidth / 2)
}
var hemiCircleCenter: CGPoint {
return CGPoint(x: frame.width / 2, y: frame.height)
}
public override func awakeFromNib() {
super.awakeFromNib()
drawProgress()
}
public override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
drawProgress()
}
public override func layoutSubviews() {
super.layoutSubviews()
drawProgress()
}
func drawProgress() {
layer.sublayers = []
layer.addSublayer(greyStrokeLayer)
layer.addSublayer(tipLayer)
layer.addSublayer(gradientLayer)
layer.addSublayer(backgroundLayer)
clipsToBounds = true
}
var greyStrokeLayer: CAShapeLayer {
let greyStroke = CAShapeLayer()
greyStroke.path = UIBezierPath(arcCenter: hemiCircleCenter, radius: backgroundRadius, startAngle: 0, endAngle: CGFloat.pi, clockwise: false).cgPath
greyStroke.lineWidth = lineWidth * 2
greyStroke.fillColor = nil
greyStroke.strokeColor = UIColor.separator.cgColor
return greyStroke
}
var gradientLayer: CAGradientLayer {
let gradient = CAGradientLayer()
gradient.frame = CGRect(x: 0, y: 0, width: frame.width * CGFloat(progress), height: frame.height)
gradient.colors = [startColor, endColor].map { $0.cgColor }
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
let progressBarLayer = CAShapeLayer()
progressBarLayer.path = UIBezierPath(arcCenter: hemiCircleCenter, radius: progressBarRadius, startAngle: 0, endAngle: CGFloat.pi, clockwise: false).cgPath
gradient.mask = progressBarLayer
return gradient
}
var backgroundLayer: CAShapeLayer {
let backGround = CAShapeLayer()
backGround.path = UIBezierPath(arcCenter: hemiCircleCenter, radius: backgroundRadius, startAngle: 0, endAngle: CGFloat.pi, clockwise: false).cgPath
backGround.fillColor = UIColor.secondarySystemGroupedBackground.cgColor
return backGround
}
var tipLayer: CAShapeLayer {
let tip = CAShapeLayer()
tip.frame = bounds
tip.path = UIBezierPath(arcCenter: hemiCircleCenter, radius: tipRadius, startAngle: -CGFloat.pi, endAngle: (CGFloat(progress) - 1) * CGFloat.pi, clockwise: true).cgPath
tip.strokeColor = endColor.cgColor
tip.fillColor = nil
tip.lineCap = .round
tip.lineWidth = lineWidth
return tip
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment