I have the following code, which I am implementing to download a PDF with a progress circle.
It is working for me, first I check that the file is already saved in the "Documents" directory and if it is true I simply show the PDF, and if the pdf file is not yet loaded then begindDownloadFile ()
lass ViewController: UIViewController, URLSessionDownloadDelegate {
@IBOutlet weak var pdfView: PDFView!
let urlString = "https://d0.awsstatic.com/whitepapers/KMS-Cryptographic-Details.pdf"
let shapeLayer = CAShapeLayer()
let percentageLabel: UILabel = {
let label = UILabel()
label.text = "Descargar"
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 16)
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(percentageLabel)
percentageLabel.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
percentageLabel.center = view.center
//start drawing circle
let center = view.center
//create the track layer, the softer color underneath the bar that it is going to fill
let trackLayer = CAShapeLayer()
let circularPath = UIBezierPath(arcCenter: .zero, radius: 100, startAngle: 0, endAngle: 2 * CGFloat.pi, clockwise: true)
trackLayer.path = circularPath.cgPath
trackLayer.strokeColor = UIColor.lightGray.cgColor
trackLayer.lineWidth = 10 //the width of the bar
trackLayer.fillColor = UIColor.clear.cgColor //amek the middle area have clear color
trackLayer.lineCap = kCALineCapRound //this to make the bar has rounded corner
trackLayer.position = center
trackLayer.opacity = 1
view.layer.addSublayer(trackLayer)
// let circularPath = UIBezierPath(arcCenter: center, radius: 100, startAngle: -CGFloat.pi / 2, endAngle: 2 * CGFloat.pi, clockwise: true) //start angle like this to have the bar start at 12 o'clock
shapeLayer.path = circularPath.cgPath
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.lineWidth = 10 //the width of the bar
shapeLayer.fillColor = UIColor.clear.cgColor //amek the middle area have clear color
shapeLayer.lineCap = kCALineCapRound //this to make the bar has rounded corner
shapeLayer.strokeEnd = 0
shapeLayer.position = center
shapeLayer.transform = CATransform3DMakeRotation(-CGFloat.pi / 2, 0, 0, 1)
view.layer.addSublayer(shapeLayer)
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
let myUrl = URL(string: urlString)
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent((myUrl?.lastPathComponent)!)
print(destinationUrl)
if FileManager.default.fileExists(atPath: destinationUrl.path) {
print("The file already exists at path")
// hidden label and remove sublayers (circle)
//self.percentageLabel.isHidden = true
//shapeLayer.removeFromSuperlayer()
//trackLayer.removeFromSuperlayer()
trackLayer.removeFromSuperlayer()
/************** show pdf ****************/
let pdfUrl = destinationUrl.path
let rutafile = URL(fileURLWithPath: pdfUrl)
print(pdfUrl)
if let document = PDFDocument(url: rutafile) {
self.pdfView.autoScales = true
self.pdfView.document = document
}
/************** show pdf ****************/
} else{
begindDownloadFile()
}
} // end DidLoad
//the require func for URLSessionDownloadDelegate delegate
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("finish downloading file")
}
//optional func from the delegate URLSessionDownloadDelegate
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
// print(totalBytesWritten, totalBytesExpectedToWrite)
let percentage = CGFloat(totalBytesWritten) / CGFloat(totalBytesExpectedToWrite)
print("percentage : ", percentage)
//this need to be in the main thread because the url session downloading is not on the main thread, so if without DispatchQueue.main, then the UI wont update
DispatchQueue.main.async {
self.percentageLabel.text = "\(Int(percentage * 100))%"
self.shapeLayer.strokeEnd = percentage
}
}
private func begindDownloadFile(){
//this fix the bar going back and forth
shapeLayer.strokeEnd = 0
let configuration = URLSessionConfiguration.default
let operationQueue = OperationQueue()
let urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: operationQueue)
guard let url = URL(string: urlString) else { return }
let downloadTask = urlSession.downloadTask(with: url)
/****/
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(url.lastPathComponent)
print(destinationUrl)
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: url, completionHandler: { (location, response, error) -> Void in
guard let location = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: location, to: destinationUrl)
print("File moved to documents folder")
print("file has already been downloaded")
DispatchQueue.main.async {
// escondemos label y removemos sublayers (circulo)
self.percentageLabel.isHidden = true
//self.shapeLayer.removeFromSuperlayer()
//self.trackLayer.removeFromSuperlayer()
//self.shapeLayer.removeAllAnimations()
self.shapeLayer.opacity = 0
/************** show pdf ****************/
let pdfUrl = destinationUrl.path
let rutafile = URL(fileURLWithPath: pdfUrl)
print(pdfUrl)
if let document = PDFDocument(url: rutafile) {
self.pdfView.autoScales = true
self.pdfView.document = document
}
/************** show pdf ****************/
}
} catch let error as NSError {
print(error.localizedDescription)
}
}).resume()
/***/
downloadTask.resume()
}
fileprivate func animateCircle() {
let basicAnimation = CABasicAnimation(keyPath: "strokeEnd") //animate the shapeLayer.strokeEnd
basicAnimation.toValue = 1
basicAnimation.duration = 2
//need these 2 lines to make the bar stopped at the final point, if not it will be removed upon completion
basicAnimation.fillMode = kCAFillModeForwards
basicAnimation.isRemovedOnCompletion = false
shapeLayer.add(basicAnimation,forKey: "customString")
}
@objc private func handleTap(){
print("animate here")
//begindDownloadFile()
// animateCircle()//custom string for forKey value, not sure where will use it later
}
}
My problem is that I can not hide the circle, I have tried inside DispatchQueue.main.async
withself.shapeLayer.removeFromSuperlayer ()
and it does not work for me, either withself.shapeLayer.opacity = 0
I would appreciate any suggestions
Your shapeLayer is red, your trackLayer is gray. What you are showing in the image is your shapeLayer removed but your trackLayer persisting which you would also need to call removeFromSuperlayer.
Also move the initial declaration of your trackLayer out of your viewDidLoad() and put it with your shapeLayer declaration so you can access it outside just that method. ie:
let urlString = "https://d0.awsstatic.com/whitepapers/KMS-Cryptographic-Details.pdf"
let shapeLayer = CAShapeLayer()
let trackLayer = CAShapeLayer()