UICollectionView图像在滚动时混淆

时间:2018-01-05 05:38:15

标签: ios swift

我正在创建一个应用程序,其中Collection的单个单元格占据整个屏幕。每个单元格包含一个图像。这些图像从服务器下载并作为UIImage存储在自定义类(卡)中。当我从该类对象的数组中显示图像时。

当我滚动时,图像有时会在错误的单元格中闪烁。我该如何纠正?

CollectionViewController.swift

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ListCell", for: indexPath) as! ListViewCell

    cell.configure(card: ClientSingleton.cards[indexPath.row])
    cell.index = indexPath.row

    return cell
}

ListViewCell.swift

override func prepareForReuse() {
    super.prepareForReuse()
    imageView?.image = UIImage()
}

func configure(card: Card) {

    imageView?.image = UIImage()
    if let image = card.image {
        self.image = image
        self.setupImageView()
        self.setupGyroBar()
        self.setupGyro()
    } else {
        DispatchQueue.global(qos: .userInitiated).async {
            card.loadImage() { image in
                DispatchQueue.main.async {
                    self.image = image
                    self.setupImageView()
                    self.setupGyroBar()
                    self.setupGyro()
                }
            }
        }
    }
    self.edgeColor = card.edgeColor
    self.inverseEdgeColor = card.inverseEdgeColor
    self.backgroundColor = self.edgeColor
}

2 个答案:

答案 0 :(得分:1)

大概是因为你异步加载图像,当它们被重用时,单元格仍然可能处于请求的中间。您在准备重用时将图像设置为空UIImage,但请求可能在此之后完成,然后在开始另一个加载请求之前或之后很快完成。

可能最简单的解决方法是,如果您可以在重复使用单元格时取消请求(或忽略其结果)。一个可能的解决方案是OperationQueue,您可以根据需要使用它来排队和取消操作。

或者,如果您正在使用某些网络库,它可能会提供一些有用的方法来取消正在进行的请求。

答案 1 :(得分:0)

单元格在滚出视图时会被重用。在configure调用期间,在加载图像时,单元格可能会滚动出视图并重新使用。

最简单的解决方案是在图像加载完成时检查的单元格中有一些标识符。您需要检查标识符是否仍然是您期望的标识符。

<强> CollectionViewController.swift

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ListCell", for: indexPath) as! ListViewCell

    // Pass the row to the configure call so it can be used as an identifier
    cell.configure(card: ClientSingleton.cards[indexPath.row], forIndex:indexPath.row)

    return cell
}

<强> ListViewCell.swift

override func prepareForReuse() {
    super.prepareForReuse()
    imageView?.image = UIImage()
}

func configure(card: Card, forIndex index: Int) {

    // Save the index as the identifier as the first thing you do,
    // then check it in the async call.
    self.index = index

    imageView?.image = UIImage()
    if let image = card.image {
        self.image = image
        self.setupImageView()
        self.setupGyroBar()
        self.setupGyro()
    } else {
        DispatchQueue.global(qos: .userInitiated).async {
            card.loadImage() { image in
                DispatchQueue.main.async {
                    if self.index == index {
                        // The cell is still being used for this index
                        self.image = image
                        self.setupImageView()
                        self.setupGyroBar()
                        self.setupGyro()
                    }
                    // else it is being used for another, so do not set the image
                }
            }
        }
    }
    self.edgeColor = card.edgeColor
    self.inverseEdgeColor = card.inverseEdgeColor
    self.backgroundColor = self.edgeColor
}

请注意,此代码仅在行和要显示的图像之间存在对应关系时才有效。如果不是这种情况,则需要使用更好的标识符来检查单元格是否仍然正确。