hi,
unfortunately the circular path does not show up i just get the effect with an extra flash in the end of image loading
below is my code:
and here is how i use it
let progressIndicatorView = CircularLoaderView(frame: .zero)
func returnImageWithprogress(imageView:UIImageView,url:String){
imageView.addSubview(progressIndicatorView)
imageView.addConstraints(NSLayoutConstraint.constraints(
withVisualFormat: "V:|[v]|", options: .init(rawValue: 0),
metrics: nil, views: ["v": progressIndicatorView]))
imageView.addConstraints(NSLayoutConstraint.constraints(
withVisualFormat: "H:|[v]|", options: .init(rawValue: 0),
metrics: nil, views: ["v": progressIndicatorView]))
progressIndicatorView.translatesAutoresizingMaskIntoConstraints = false
let url = URL(string: url)
imageView.sd_setImage(with: url, placeholderImage: nil, options: .progressiveDownload, progress:
{ [weak self] receivedSize, expectedSize, _ in
self?.progressIndicatorView.progress = CGFloat(receivedSize) / CGFloat(expectedSize)
}) { [weak self] _, error, _, _ in
if let error = error {
print(error)
}
self?.progressIndicatorView.reveal()
}
}
import UIKit
class CircularLoaderView: UIView {
let circlePathLayer = CAShapeLayer()
let circleRadius: CGFloat = 20.0
var progress: CGFloat {
get {
return circlePathLayer.strokeEnd
}
set {
if newValue > 1 {
circlePathLayer.strokeEnd = 1
} else if newValue < 0 {
circlePathLayer.strokeEnd = 0
} else {
circlePathLayer.strokeEnd = newValue
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
func configure() {
circlePathLayer.frame = bounds
circlePathLayer.lineWidth = 2
circlePathLayer.fillColor = UIColor.clear.cgColor
circlePathLayer.strokeColor = UIColor.red.cgColor
layer.addSublayer(circlePathLayer)
backgroundColor = .white
progress = 0
}
func circleFrame() → CGRect {
var circleFrame = CGRect(x: 0, y: 0, width: 2 * circleRadius, height: 2 * circleRadius)
let circlePathBounds = circlePathLayer.bounds
circleFrame.origin.x = circlePathBounds.midX - circleFrame.midX
circleFrame.origin.y = circlePathBounds.midY - circleFrame.midY
return circleFrame
}
func circlePath() → UIBezierPath {
return UIBezierPath(ovalIn: circleFrame())
}
override func layoutSubviews() {
super.layoutSubviews()
circlePathLayer.frame = bounds
circlePathLayer.path = circlePath().cgPath
}
func reveal() {
// 1
backgroundColor = .clear
progress = 1
// 2
circlePathLayer.removeAnimation(forKey: “strokeEnd”)
// 3
circlePathLayer.removeFromSuperlayer()
superview?.layer.mask = circlePathLayer
// 1
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let finalRadius = sqrt((center.x*center.x) + (center.y*center.y))
let radiusInset = finalRadius - circleRadius
let outerRect = circleFrame().insetBy(dx: -radiusInset, dy: -radiusInset)
let toPath = UIBezierPath(ovalIn: outerRect).cgPath
// 2
let fromPath = circlePathLayer.path
let fromLineWidth = circlePathLayer.lineWidth
// 3
CATransaction.begin()
CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
circlePathLayer.lineWidth = 2*finalRadius
circlePathLayer.path = toPath
CATransaction.commit()
// 4
let lineWidthAnimation = CABasicAnimation(keyPath: "lineWidth")
lineWidthAnimation.fromValue = fromLineWidth
lineWidthAnimation.toValue = 2*finalRadius
let pathAnimation = CABasicAnimation(keyPath: "path")
pathAnimation.fromValue = fromPath
pathAnimation.toValue = toPath
// 5
let groupAnimation = CAAnimationGroup()
groupAnimation.duration = 1
groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
groupAnimation.animations = [pathAnimation, lineWidthAnimation]
groupAnimation.delegate = self
circlePathLayer.add(groupAnimation, forKey: "strokeWidth")
}
}
extension CircularLoaderView: CAAnimationDelegate {
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
superview?.layer.mask = nil
}
}