How to asynchronous load image from a web-server in UICollectionView using NSCache
I have some issues when loading images from a web-server in UICollectionView using NScache.
The problem:
The images are not proper displayed:
- sometimes they are not showned in the corresponding cell
or
- the image is changing on scroll
Situation:
I have 3 arrays whitch are properly loaded from the web-server in function viewDidLoad(). These arrays are: vPrice, vTitle and vImages_api
-
my custom class for cell have:
label for price: cell.lblPrice
label for title: cell.lblTitle
image: cell.imgPicture
I belive that the problem is in function func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
It could be related either to the way I use NSCache or to the way I use and when I use DispatchQueue.
The code:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collectionViewPRODUSE.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath) as! clsCustomCell
cell.lblPrice.text = vPrice[indexPath.item]
cell.lblTitle.text = vTitle[indexPath.item]
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 0.5
DispatchQueue.global(qos: .userInitiated).async {
//background thread
let ImageString = self.vImages_api[indexPath.item]
let imageUrl = URL(string: ImageString)
let imageData = NSData(contentsOf: imageUrl!)
// main thread to update the UI
DispatchQueue.main.async {
let key1 = self.vImages_api[indexPath.item] as AnyObject
//if i saved allready my image in cache, then i will load my image from cache
if let imageFromCache = self.objCache.object(forKey: key1){
cell.imgPicture.image = imageFromCache as! UIImage
}
else{//if my image is not in cache ......
if imageData != nil {
let myPicture = UIImage(data: imageData! as Data)
cell.imgPicture.image = myPicture
//save my image in cache
self.objCache.setObject(myPicture!, forKey: ImageString as AnyObject)
}
}
}
}
return cell
}
Edited code - version II:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collectionViewPRODUSE.dequeueReusableCell(withReuseIdentifier: "MyCustomCell", for: indexPath) as! clsCustomCell
cell.lblPret.text = vPrice[indexPath.item]
cell.lblTitlu.text = vTitle[indexPath.item]
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 0.5
let key1 = self.vImages_api[indexPath.item] as AnyObject
if let imageFromCache = self.objCache.object(forKey: key1){
cell.imgPicture.image = imageFromCache as! UIImage
}else{
DispatchQueue.global(qos: .background).async {
let ImageString = self.vImages_api[indexPath.item]
let imageUrl = URL(string: ImageString)
let imageData = NSData(contentsOf: imageUrl!)
let myPicture = UIImage(data: imageData! as Data)
self.objCache.setObject(poza!, forKey: ImageString as AnyObject)
DispatchQueue.main.async {
if imageData != nil {
cell.imgPicture.image = myPicture
}
}
}
}
return cell
}
Edited code - version III
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collectionViewPRODUSE.dequeueReusableCell(withReuseIdentifier: "celula_custom", for: indexPath) as! clsCustomCell
cell.lblPret.text = vPrice[indexPath.item]
cell.lblTitlu.text = vTitle[indexPath.item]
NKPlaceholderImage(image: UIImage(named: "loading"), imageView: cell.imgPicture, imgUrl: self.vImages_api[indexPath.item]
) { (image11) in
cell.imgPicture.image = image11
}
cell.layer.borderColor = UIColor.lightGray.cgColor
cell.layer.borderWidth = 0.5
return cell
}
Try this one it's Working code (Swift 4).
func NKPlaceholderImage(image:UIImage?, imageView:UIImageView?,imgUrl:String,compate:@escaping (UIImage?) -> Void){
if image != nil && imageView != nil {
imageView!.image = image!
}
var urlcatch = imgUrl.replacingOccurrences(of: "/", with: "#")
let documentpath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
urlcatch = documentpath + "/" + "\(urlcatch)"
let image = UIImage(contentsOfFile:urlcatch)
if image != nil && imageView != nil
{
imageView!.image = image!
compate(image)
}else{
if let url = URL(string: imgUrl){
DispatchQueue.global(qos: .background).async {
() -> Void in
let imgdata = NSData(contentsOf: url)
DispatchQueue.main.async {
() -> Void in
imgdata?.write(toFile: urlcatch, atomically: true)
let image = UIImage(contentsOfFile:urlcatch)
compate(image)
if image != nil {
if imageView != nil {
imageView!.image = image!
}
}
}
}
}
}
}
Use Like this :
// Here imgPicture = your imageView and UIImage(named: "placeholder") is Display image brfore download actual image.
imgPicture.image = nil
NKPlaceholderImage(image: UIImage(named: "placeholder"), imageView: imgPicture, imgUrl: "Put Here your server image Url Sting") { (image) in }