csv文件
csv file is @ url https://myserver.com/csv.txt and contains the following Title";"SeriesImageURL Title1";"https://licensedimage.com/url1 Title2";"https://licensedimage.com/url2 ... Title1000";"https://licensedimage.com/url1000
问题是当快速滚动CollectionView时,单元格将抓取不正确的图像.值得注意的是,如果您进行慢速或中速滚动,则在将正确的图像渲染到正确的单元格之前将显示不同的图像(单元格的标签文本始终是正确的,只有图像永远关闭).在图像与具有标签的正确单元格不匹配之后,CollectionView中的所有其他单元格也将显示不正确的图像.
例如.单元格1-9将显示具有正确Image1-9的Title1-9
当缓慢滚动时,单元格19-27将显示标题19-27,将简要显示图像10-18,然后显示正确的图像19-27.
当快速滚动大量单元格时(例如从单元格1-9到单元格90-99),单元格90-99将显示标题90-99,将显示图像10-50ish,然后将错误地保留在图像41-50 (或左右).当进一步滚动时,单元格100将显示正确的标题,但仅显示图像41-50范围内的图像.
我认为这个错误或者是因为没有正确处理单元重用,没有正确处理图像的缓存,或者两者兼而有之.它也可能是我作为初学者iOS / swift程序员看不到的东西.我试图用完成修饰符实现一个请求,但似乎无法使我的代码设置方式正常工作.我将不胜感激任何帮助,并解释为什么修复工作的方式.谢谢!
相关代码如下.
SeriesCollectionViewController.swift
class SeriesCollectionViewController: UICollectionViewController,UISearchBarDelegate { let reuseIdentifier:String = "SeriesCell" // Set Data Source Models & Variables struct seriesModel { let title: AnyObject let seriesimageurl: AnyObject } var seriesDict = [String:AnyObject]() var seriesArray = [seriesModel]() // Image Cache var imageCache = NSCache() override func viewDidLoad() { super.viewDidLoad() // Grab Data from Source do { let url = NSURL(string: "https://myserver.com/csv.txt") let fullText = try NSString(contentsOfURL: url!,encoding: NSUTF8StringEncoding) let readings = fullText.componentsSeparatedByString("\n") as [String] var seriesDictCount = readings.count seriesDictCount -= 1 for i in 1..<seriesDictCount { let seriesData = readings[i].componentsSeparatedByString("\";\"") seriesDict["Title"] = "\(seriesData[0])" seriesDict["SeriesImageURL"] = "\(seriesData[1])" seriesArray.append(seriesModel( title: seriesDict["Title"]!,seriesimageurl: seriesDict["SeriesImageURL"]!,)) } } catch let error as NSError { print("Error: \(error)") } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() imageCache.removeAllObjects() // Dispose of any resources that can be recreated. } //... //...skipping over some stuff that isn't relevant //... override func collectionView(collectionView: UICollectionView,cellForItemAtIndexPath indexPath: NSIndexPath) -> SeriesCollectionViewCell { let cell: SeriesCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier,forIndexPath: indexPath) as! SeriesCollectionViewCell if (self.searchBarActive) { let series = seriesArrayForSearchResult[indexPath.row] do { // set image if let imageURL = NSURL(string: "\(series.seriesimageurl)") { if let image = imageCache.objectForKey(imageURL) as? UIImage { cell.seriesImage.image = image } else { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0),{ if let tvimageData = NSData(contentsOfURL: imageURL) { let image = UIImage(data: tvimageData) self.imageCache.setObject(image!,forKey: imageURL) dispatch_async(dispatch_get_main_queue(),{ () -> Void in cell.seriesImage.image = nil cell.seriesImage.image = image }) } }) } } cell.seriesLabel.text = "\(series.title)" } } else { let series = seriesArray[indexPath.row] do { // set image if let imageURL = NSURL(string: "\(series.seriesimageurl)") { if let image = imageCache.objectForKey(imageURL) as? UIImage { cell.seriesImage.image = image } else { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,forKey: imageURL) dispatch_async(dispatch_get_main_queue(),{ () -> Void in cell.seriesImage.image = nil cell.seriesImage.image = image }) } }) } } cell.seriesLabel.text = "\(series.title)" } } cell.layer.shouldRasterize = true cell.layer.rasterizationScale = UIScreen.mainScreen().scale cell.prepareForReuse() return cell }
SeriesCollectionViewCell
class SeriesCollectionViewCell: UICollectionViewCell { @IBOutlet weak var seriesImage: UIImageView! @IBOutlet weak var seriesLabel: UILabel! }
总结一下:
To maintain scrolling smoothness,dequeue is used which essentially
reuses cells after a certain limit. Say you have 10 visible cells at a
time,it will likely create 16-18 (3-4 above,3-4 below,just rough
estimates) cells only,even though you might need 1000 cells.
现在,当你这样做时 –
let cell: SeriesCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier,forIndexPath: indexPath) as! SeriesCollectionViewCell
您正在重用已经制作的单元格中的现有单元格.这就是为什么你会看到旧图像一段时间然后在加载后看到新图像的原因.
您需要在单元格出列后立即清除旧图像.
cell.seriesImage.image = UIImage() //nil
实际上,您将在图像中以这种方式设置一个空白占位符,而不是在您的情况下将imageCache设置为nil.
这解决了这个问题 –
Noticeably,if you do a slow or medium scroll,a different image will show before the correct image is rendered into the correct cell (the label text for the cells are always correct,only the image is ever off).
现在,在为当前单元格分配图像时,如果要分配的图像属于此单元格,则需要再次检查.您需要检查这一点,因为您正在重新分配它的图像视图,它可能与indexPath上的单元格关联,而不是生成图像加载请求时的单元格.
当您将单元格出列并将image属性设置为nil时,请让seriesImage为您记住当前的indexPath.
cell.seriesImage.indexPath = indexPath
稍后,如果imageView仍属于先前分配的indexPath,则仅将图像分配给它.这与细胞重用100%有效.
if cell.seriesImage.indexPath == indexPath cell.seriesImage.image = image
您可能需要考虑在UIImageView实例上设置indexPath.这是我准备并用于类似场景的东西 –
UIView-Additions
它既有Objective-C又有.迅速.