我有一个带有用户列表及其个人资料图片的Uitable-view。我正在加载图片(http://api/pictures/{userId}),每个播放器异步:
func loadImageAsync(imageUrl: URL, completionHandler handler: @escaping (_ success: Bool, _ image: UIImage?) -> Void){
DispatchQueue.global(qos: .userInitiated).async { () -> Void in
if let imgData = try? Data(contentsOf: imageUrl), let img = UIImage(data: imgData) {
DispatchQueue.main.async(execute: { () -> Void in
handler(true, img)
})
} else {
handler(false, nil)
}
}
在Cellforrowat-Index-Fuction的完成处理程序中,我正在设置图片。
loadImageAsync(imageUrl: imageUrl!, label: ip) { (success, image, backlabel) -> Void in
if(success){
cell.profilePictureView.image = image
}
}
但是,当我快速滚动时,有些图片会加载在错误的单元格中。为了防止重复使用,我正在"重复使用"后"重置"图像视图:
override func prepareForReuse() {
profilePictureView.image = UIImage(named: "defaultProfilePicture")
}
但是,为什么还要快速滚动时仍然有些图像false?
嗯,这也是我的想法。
__更新:因此,我使用标签参数(键入Any)扩展了该功能,该参数被放入功能中时会返回。我试图将参数(用于indexpath)与当前的indexpath进行比较。实际上,这应该有效 - 不是吗?!
loadImageAsync(imageUrl: imageUrl!, label: ip) { (success, image, backlabel) -> Void in
cell.loader.stopAnimating()
if (backlabel as! IndexPath == indexPath) {
//set image...
但是,它没有任何效果。您知道为什么或还有其他解决方案可以解决此问题吗?
问题是,如果您快速滚动,下载可能会花费足够长的时间,以至于到完成时,所讨论的单元格已经从屏幕上滚下来,并已回收为不同的Indexpath您的数据模型。
诀窍是在完成块中的该indexpath处向单元格询问表视图,仅在收回单元格时才安装图像:
loadImageAsync(imageUrl: imageUrl!, label: ip, for indexPath: IndexPath) { (success, image, backlabel) -> Void in
if(success){
let targetCell = tableview.cell(for: indexPath)
targetCell.profilePictureView.image = image
}
}
编辑:
重新定义您的loadImageasync函数:
func loadImageAsync(imageUrl: URL,
indexPath: IndexPath,
completionHandler handler: @escaping (_ success: Bool,
_ image: UIImage?,
_ indexPath: IndexPath ) -> Void) { ... }
编辑#2
,顺便说一句,您应该真正保存图像来磁盘并从那里加载它们,而不是每次从Internet加载。我建议将图像URL的哈希作为文件名。
修改loadImageasync如下:
检查文件是否已经存在于磁盘上。如果是这样,请加载并返回。
如果文件不存在,请执行异步加载,然后在返回内存映像之前,将其保存到磁盘上。
。
,因为在为下一个用户重复使用单元格之后,可以调用您的完整汉格勒,并且可能已经触发了单元格的另一个图像请求。事件的顺序(重复/完成)是无法预测的,实际上,以后的异步请求可以在较早的一个请求之前完成。