I am downloading a couple of images and cache them. Then I am recalling the cached images and add them as a subView to my scrollView (which is horizontal using a page controller). I want to do both of that when view appears without any other actions required. Unfortunately, it already adds the subview before the images were cached, because it goes through the code while the images are getting downloaded. It follows that nothing is added as a subView.
It works when I download and cache the images when the view appears and I add them as a subView when I press a button, but I would like for that to work automatically.
I am not sure if my code helps for a better understanding but here it is.
This is my code to download the images
func downloadImage(url: URL) {
let dataTask = URLSession.shared.dataTask(with: url) { data, responseURL, error in
var downloadedImage:UIImage?
if let data = data {
downloadedImage = UIImage(data: data)
}
// if download actaully got an image
if downloadedImage != nil {
self.sum += 1
self.cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString) // cache the image
// add the url as value to the dictionary
self.imageURLS[self.sum] = url.absoluteString
}
}
dataTask.resume()
}
And this is what I use to add the subviews
func appendImages(){
// sort the values of the dictionary by the greatest
let sortedImageURLs = Array(imageURLS.keys).sorted(by: >)
var stringURLs = [String]() // empty string array to append the URL's
// for loop which goes through all integers in "creation Date" array
for keys in sortedImageURLs {
let url: String? = imageURLS[keys] // url which is connected to the key
stringURLs.append(url!) // append the url to the url array
}
// -> the url string array starts with the latest loaded url
var originSubview = 0 // counter of the origin of the subviews
for urls in stringURLs {
// 1.
frame.origin.x = scrollView.frame.size.width * CGFloat(originSubview)
frame.size = scrollView.frame.size
// 2.
let imageView = UIImageView(frame: frame)
// get the image which is cahed under the url
let image = cache.object(forKey: urls as NSString)
// set the image of image view as the cached image
imageView.image = image
// and add that image view as a subview to the scrollView
scrollView.addSubview(imageView)
// increase counter variable by one
//-> next images origin is at the end of the image before
originSubview += 1
}
// 3.
scrollView.contentSize = CGSize(width: ((scrollView.frame.size.width) * CGFloat(specialsCounter)), height: (scrollView.frame.size.height))
scrollView.delegate = self
pageControl.numberOfPages = specialsCounter
}
You can use a DispatchGroup to notify when all your downloads have completed.
Basically you enter()
before you start a download and leave
when the download has finished. notify
will trigger when all entered
tasks has left
Here is an example on how to do it. I made a completion block for your downloadImage function.
let dispatchGroup = DispatchGroup()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
for url in imageUrls {
self.dispatchGroup.enter()
downloadImage(url: url) {
self.dispatchGroup.leave()
}
}
self.dispatchGroup.notify(queue: .main) {
//All images has been downloaded here
}
}
downloadImage function with completion:
func downloadImage(url: URL, completion: () -> ()) {
let dataTask = URLSession.shared.dataTask(with: url) { data, responseURL, error in
var downloadedImage:UIImage?
if let data = data {
downloadedImage = UIImage(data: data)
}
// if download actaully got an image
if downloadedImage != nil {
self.sum += 1
self.cache.setObject(downloadedImage!, forKey: url.absoluteString as NSString) // cache the image
// add the url as value to the dictionary
self.imageURLS[self.sum] = url.absoluteString
completion()
} else {
completion()
}
}
dataTask.resume()
}