swift iOS - 快速滚动后混合的UICollectionView图像 [英] swift iOS - UICollectionView images mixed up after fast scroll

查看:102
本文介绍了swift iOS - 快速滚动后混合的UICollectionView图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是swift和iOS编程的新手,但我已经能够在Xcode中为我的应用程序构建一个最稳定的启动界面。 CollectionView从我家庭网络服务器上的csv文件创建的字典数组中获取图像和文本。 cvs文件包含以下格式的数据(注意,网址已更改以保护许可图像):

I am new to swift and iOS programming, but I have been able to build a mostly stable starting interface for my application in Xcode. The CollectionView grabs an image and text from an array of dictionaries created from a csv file on my home network server. The cvs file contains data in the following format (note, urls have been changed to protect licensed images):

csv file

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中的所有其他单元格也将显示不正确的图像。

The problem is that when scrolling quickly through the CollectionView, the cell will grab the incorrect image. 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). After the mismatch of images to proper cell with label occurs, all other cells in the CollectionView will also have incorrect images displayed.

例如。单元格1-9将显示Title1-9与正确的Image1-9
当缓慢滚动时,单元格19-27将显示标题19-27,将简要显示图像10-18,然后显示正确的图像19-27。
当快速滚动大量单元格时(例如从单元格1-9到单元格90-99),单元格90-99将显示标题90-99,将显示图像10-50ish,然后将错误地保持在图41-50(或左右)。当进一步滚动时,单元格100+将显示正确的标题,但仅显示图像41-50范围内的图像。

E.g. Cell 1-9 will show Title1-9 with correct Image1-9 When scrolling slowly, Cells 19-27 will show Title 19-27, will briefly show Image 10-18 and then show the correct Image 19-27. When scrolling quickly a huge number of cells (e.g. from cell 1-9 to cell 90-99), Cells 90-99 will show Title 90-99, will show Image 10-50ish, and then will incorrectly stay on Image 41-50(or thereabout). When scrolling further, Cells 100+ will display the correct Title but will only show images from the range Image 41-50.

我认为这个错误或者是因为没有正确处理单元重用,没有正确处理图像的缓存,或者两者兼而有之。它也可能是我作为初学者iOS / swift程序员看不到的东西。我试图用完成修饰符实现一个请求,但似乎无法使我的代码设置方式正常工作。我将不胜感激任何帮助,并解释为什么修复工作的方式。谢谢!

I think this error is either because the cell reuse isn't handled properly, the caching of images isn't handled properly, or both. It could also be something I am not seeing as a beginner iOS/swift programmer. I have tried to implement a request with a completion modifier but cannot seem to get it working properly with the way my code is set up. I would appreciate any help with this as well as an explanation for why the fix works the way it does. Thanks!

相关代码如下。

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, 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)"
        }
    }
    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!

}


推荐答案

你需要了解出队如何正常运作。有非常好的文章。

You need to understand how dequeue works properly. There are very good articles for this.

总结:


维持滚动平滑度,使用出局,基本上
在一定限制后重复使用单元格。假设您在
时间内有10个可见细胞,它可能会产生16-18(3-4以上,3-4以下,仅粗略
估计)细胞,即使您可能需要1000个细胞。

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.

现在当你这样做时 -

Now when you are doing this-

let cell: SeriesCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! SeriesCollectionViewCell

您正在重复使用已制作单元格中的现有单元格。这就是为什么你会看到旧图像一段时间然后在加载后看到新图像的原因。

You are reusing an existing cell from already made cells. That's why you see old image for some time and then you see new image once it's loaded.

你需要在单元格出列后立即清除旧图像。

You need to clear the old image as soon as you dequeue the cell.

cell.seriesImage.image = UIImage()    //nil

你基本上会在图像中以这种方式设置一个空白占位符,而不是在你的情况下使用imageCache为零。

You will essentially set a blank placeholder this way in the image and not mess with imageCache being nil in your case.

解决了这个问题 -

That solves this problem-


值得注意的是,如果你进行慢速或中速滚动,在正确的图像之前会显示不同的图像渲染到正确的单元格(单元格的标签文本总是正确的,只有图像永远关闭)。

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上与生成图像加载请求时不同的单元格相关联。

Now when assigning the image for current cell, you need to check again, if the image you're assigning belongs to this cell or not. You need to check this because the image view you're assigning it to is being reused and it might be associated to a cell at an indexPath different from that of when the image load request was generated.

当您将单元格出列并将image属性设置为nil时,请让seriesImage为您记住当前的indexPath。

When you a dequeue the cell and set the image property to nil, ask the seriesImage to remember the current indexPath for you.

cell.seriesImage.indexPath = indexPath

稍后,您只需将图像分配给它,如果imageView仍然属于以前分配的indexPath。这可以100%用于单元格重用。

Later, you only assign the image to it, if the imageView still belongs to previously assigned indexPath. This works 100% with cell reuse.

if cell.seriesImage.indexPath == indexPath
cell.seriesImage.image = image

您可能需要考虑在UIImageView实例上设置indexPath。这是我准备并用于类似场景的东西 -
UIView-Additions

You might need to consider setting the indexPath on UIImageView instance. Here's something I prepared and use for a similar scenario- UIView-Additions

Objective-C&斯威夫特。

It is available in both Objective-C & Swift.

这篇关于swift iOS - 快速滚动后混合的UICollectionView图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆