【发布时间】:2019-01-15 12:52:07
【问题描述】:
我在使用 NScache 从 UICollectionView 中的网络服务器加载图像时遇到了一些问题。
问题:
图片显示不正确:
- 有时它们不会显示在相应的单元格中
或
- 图像在滚动时发生变化
情况:
我有 3 个数组在函数 viewDidLoad() 中从 Web 服务器正确加载。这些数组是:vPrice、vTitle 和 vImages_api
-
我的单元格自定义类有:
价格标签:cell.lblPrice
标题标签:cell.lblTitle
图片:cell.imgPicture
我相信问题出在函数 func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
这可能与我使用 NSCache 的方式有关,也可能与我使用的方式和使用 DispatchQueue 的时间有关。
代码:
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
}
编辑后的代码 - 版本 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
}
编辑后的代码 - 版本 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
}
【问题讨论】:
-
“我有 3 个数组”:糟糕的设计。这 3 个项目需要同步,因此使用一个字典数组或自定义结构,其中一个对象可以保存价格值、标题值和 imageLink 之一。
let imageData = NSData(contentsOf: imageUrl!)不要。这会阻塞线程,即使它在后台。如果缓存中有图像,您应该事先检查。由于您正在做的两件事,您的代码不清楚吗?那真的是你的代码吗?还是两次尝试? -
正如你所建议的,我在 DispatchQueue 中使用之前检查了图像是否存储在缓存中。现在,在编辑代码后,在版本 II 中出现了一个新情况:有时图像在相应的单元格中正确显示,并且在滚动时不再更改。但有时,在刷新时,图像仍然显示在错误的单元格中,并且缺少一些相应的描述。
-
您应该在拨打
DispatchQueue.global(qos: .background).async之前执行cell.imgPicture = nil(或将其替换为占位符)。因为单元格被重复使用,所以在下载开始到结束之间,图像可能有一个较旧的图像。复用是UITableView/UICollectionView的一个重要概念。如果它解决了你的问题,现在大多数错误都被清除了(如果NSData(contentsOf:)似乎仍然是一种不使用的方法,我更喜欢URLSession因为错误返回),我建议你使用SDWebImage或同化这样做等等。
标签: uicollectionview swift4 grand-central-dispatch