There are some ways for creating a circular progress bar in iOS (Example).
But I'd like to achieve an effect where the head of the progressbar is rounded and the progress alpha fades along the way. Something like this:
Any ideas how can achieve this kind of effects?
One approach is to mask a CAGradientLayer
with a CAShapeLayer
. The shape layer should have an arc as its path
, and a lineCap
of .round
.
class FadingProgressView: UIView {
let shapeLayer = CAShapeLayer()
let gradientLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .clear
layer.addSublayer(gradientLayer)
gradientLayer.mask = shapeLayer
gradientLayer.colors = [UIColor.blue.cgColor, UIColor.white.cgColor, UIColor.white.cgColor]
gradientLayer.type = .conic
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.4, y: 0)
gradientLayer.locations = [0, 0.75, 1]
gradientLayer.backgroundColor = UIColor.clear.cgColor
shapeLayer.fillColor = nil
shapeLayer.strokeColor = CGColor(gray: 0, alpha: 1)
shapeLayer.lineWidth = 5
shapeLayer.lineCap = .round
let animation = CABasicAnimation(keyPath: "transform.rotation")
animation.fromValue = CGFloat.pi * 2
animation.toValue = 0
animation.duration = 1
animation.repeatCount = .greatestFiniteMagnitude
layer.add(animation, forKey: "transform.rotation")
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
shapeLayer.frame = bounds
let path = UIBezierPath(
arcCenter: .init(x: bounds.midX, y: bounds.midY),
radius: bounds.width / 2 - 20,
startAngle: 1.55 * .pi, endAngle: .pi, clockwise: true)
shapeLayer.path = path.cgPath
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Usage:
class MyViewController: UIViewController {
override func viewDidLoad() {
let progress = FadingProgressView(frame: CGRect(x: 200, y: 200, width: 100, height: 100))
view.addSubview(progress)
}
}