gpt4 book ai didi

ios - UICollectionView 出列错误的单元格并显示错误的图像

转载 作者:行者123 更新时间:2023-11-28 07:19:07 28 4
gpt4 key购买 nike

我有一个 UICollectionView,我在其中显示图像或带有播放按钮的视频图像。单元格占据屏幕的整个宽度,因此同一时间只有一个单元格在屏幕上可见。用户可以向左或向右滚动以显示下一个图像或视频。我最能将它与 Instagram 的提要进行比较。

因此,当用户滚动时,总会有一张图片显示给用户。但是,当我在单元格中滚动时,比方说,有四张或五张图片时,它们会混淆。这是我的代码:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "mediaSliderCell", for: indexPath) as! MediaSliderCell
if(dataArray.count > 0) {
if(dataArray[indexPath.item].mediaType == .photo) {
let mediaURL = URL(string: "https://myURL.com/media/\(dataArray[indexPath.item].mediaURL)")
cell.photoView.kf.setImage(with: mediaURL, options: [.targetCache(mediaCache)])
cell.photoView.isHidden = false
cell.videoView.isHidden = true
} else if(dataArray[indexPath.item].mediaType == .video) {
cell.photoView.isHidden = true
cell.videoView.isHidden = false
let mediaThumbURL = URL(string: "https://myURL.com/media/\(dataArray[indexPath.item].mediaThumbURL!)")
let mediaURL = URL(string: "https://myURL.com/media/\(dataArray[indexPath.item].mediaURL)")!
cell.videoView.placeholderView.kf.setImage(with: mediaThumbURL, options: [.targetCache(mediaCache)])
cell.videoView.mediaURL = mediaURL
cell.videoView.testLabel.text = "indexPath.item: \(indexPath.item)"
}
}
return cell
}

我知道出列是如何工作的。过去我遇到过这个问题。到目前为止,我还尝试了我所知道的一切来避免这个问题:

  • 在 prepareForReuse() 中将 imageView.image 设置为 nil
  • 使用 if else 语句并在 cellForItemAt 中将 imageView.image 设置为 nil,但这会返回黑屏(因此此时没有图像)

当我使用视频时会发生这种情况。对于照片,我还没有看到,但也可能是这种情况。我该如何解决这个问题?

这是我的MediaSliderCell:

class MediaSliderCell: UICollectionViewCell {
override func prepareForReuse() {
videoView.placeholderView.kf.cancelDownloadTask()
videoView.placeholderView.image = UIImage()
photoView.image = UIImage()
}

var photoView: UIImageView = {
let photoView = UIImageView()
photoView.translatesAutoresizingMaskIntoConstraints = false
photoView.backgroundColor = .black
photoView.isHidden = true
return photoView
}()

var videoView: VideoView = {
let videoView = VideoView()
videoView.translatesAutoresizingMaskIntoConstraints = false
videoView.backgroundColor = .black
return videoView
}()

override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}

func setupViews() {
addSubview(photoView)
addSubview(videoView)
photoView.topAnchor.constraint(equalTo: topAnchor).isActive = true
photoView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
photoView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
photoView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
videoView.topAnchor.constraint(equalTo: topAnchor).isActive = true
videoView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
videoView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
videoView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
}

required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

VideoView 也是自定义的,但这只是 AVPlayer 的实现,不应影响单元格的行为。这是相当多的代码,所以我不想发布它,因为它不处理显示的图像。知道这里发生了什么吗?当我在 cellForItemAt 中打印 indexPaths 和 URL 时,我总是得到我应该看到的图像的正确 URL。但是图像混淆了。我使用 KingFisher 下载图像并缓存它们以备将来使用。

编辑:基本上,据我了解目前的情况,在 cellForItemAt 中,返回了正确的数据(至少当我打印出来时)。我确实注意到,只要 iOS 想要检索它们,就会检索单元格,因此 iOS 可以为单元格 0、然后是单元格 1、然后是单元格 2、然后是单元格 1 获取数据……但这不应该是问题。

首先,我认为问题可能出在 KingFisher 下载方法中,因为当单元格已更改时,下载可能会稍后完成。但是话又说回来,该单元格也使用了错误的 mediaURL,因此不仅图像是错误的,而且我链接到它的 mediaURL 也是错误的(mediaURL 例如是 https://myURL.com/media/test.mp4,mediaThumbURL 例如是 https://myURL.com/media/thumb/test.jpeg)。我认为问题出在其他地方,也许在 VideoView 中?这会被重复使用或以某种方式错误关联吗?

提及我的 dataArray 是这样构建的可能也会有所帮助:

struct MediaData: Codable {
var mediaType: MediaType // enum .photo or .video
var mediaURL: String
var mediaThumbURL: String?
}

接下来,我简单地追加我的所有项目,因此我需要以正确的顺序显示它们,这就是我依赖 indexPath.item 正确地从数组中挑选它们的原因。

EDIT2:我现在注意到返回的 indexPath 有时也是错误的。我在单元格中添加了一个 UILabel 并添加了 cell.testLabel.text = indexPath.item。有时,对于第一个单元格(应该是 indexPath 项目 0),结果是 3。所以第一个单元格,在左右滚动之后,然后有 indexPath (0,3)。为什么会这样?我该如何解决?

最佳答案

在对 KingsFisher 库进行一些研究后,我发现,如果图像的缓存是磁盘缓存,则 cancelDownloadTask() 不会阻止 KingsFisher 从磁盘缓存中检索图像。同样对于磁盘缓存,它会将获取的数据切换到另一个 DispatchQueue 并且 kf.setImage 会在一段时间后从磁盘缓存设置图像。这导致了有趣的“行为”(我会说这不是记录在案的行为或可能是错误)。
如何重现它:
1. 运行您的应用。
2. 滚动到收藏 View 的末尾。
3. 等待一段时间(直到所有图像都下载并缓存到磁盘)。
4. 重新启动您的应用。
5. 等待图像从磁盘缓存加载并显示在您的第一个 UICollectionViewCell 中。
6. 向右滚动到第二个单元格,无需等待即可返回到第一个单元格。
7. 您将看到来自第二个单元格而不是第一个单元格的图像。
原因是如果 KingsFisher 同步从内存缓存中获取图像。但对于磁盘缓存,它使其与另一个 DispatchQueue 异步。

// This url is loaded from disk cache
let url1 = URL(string: "some url 1")
// This url is loaded from memory cache
let url2 = URL(string: "some url 2")

// Because url1 is taken from disk cache
// it would be setted asynchronously in future.
imageView.kf.setImage(with: url1)

// Becase url2 is taken from memory cache
// it setted synchronously in present.
imageView.kf.setImage(with: url2)

// As result imageView constains image from url1 instead of image from url2.

/*
One of the solutions is to use .fromMemoryCacheOrRefresh option
that would igrnore disk cache, and load images synchronously from memory cache.

imageView.kf.setImage(with: url1, options: [.fromMemoryCacheOrRefresh])
imageView.kf.setImage(with: url2, options: [.fromMemoryCacheOrRefresh])

imageView will show image from url2.
*/

此外,您在 setupViews 中必须将标签和视频 View 放置在它的 contentView 上而不是单元格上。

contentView.addSubview(photoView)
contentView.addSubview(videoView)

关于ios - UICollectionView 出列错误的单元格并显示错误的图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58666601/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com