如何修复滚动时从 UICollectionViewCell 中消失的图像
Posted
技术标签:
【中文标题】如何修复滚动时从 UICollectionViewCell 中消失的图像【英文标题】:How to fix images disappearing from UICollectionViewCell on scroll 【发布时间】:2019-07-21 18:35:12 【问题描述】:在此处输入代码我正在创建一个需要在 UICollectionViewCell
中使用 UICollectionView
的应用程序。我已经成功地在单元格中添加了UICollectionView
及其所有委托方法,并且在初始加载时,一切都正确加载。然而,一旦用户尝试在主UICollectionView
中进一步向下滚动,包含内部UICollectionView
的单元格基本上就会失去他们的视野。需要注意的一个有趣细节是,只有当用户以非蜗牛速度滚动时,问题才会出现。如果滚动速度足够慢,单元格将正确加载。否则,单元格将变得畸形。
不幸的是,我处理这个问题已经有一段时间了,目前已经接近/一个多月了。我已经尝试了 ***、Medium 文章、YouTube 教程等上提出的几乎所有解决方案,但还没有找到可行的解决方案。
如果我总是将UICollectionView
添加为子视图,则此行为不会在没有条件渲染的情况下表现出来。
需要注意的一个重要细节是我的单元格是垂直调整大小的,并且不保证每个单元格都包含一个内部UICollectionView
。我怀疑我的问题的一部分源于我对这种条件渲染的处理(即,如果单元格的数据源有图像,则将 UICollectionView
添加为子视图。否则,根本不要添加它)。
此外,我在我的 Xcode 项目中使用导入的资产作为图像。我没有进行任何异步调用,否则可能归因于未获取的图像和数据源有 0 个图像,而它应该具有 > 0。我在 parseDatasource(withDatasource datasource: (String, [UIImage])
函数中的日志显示 datasource.1
包含图像数组正如预期的那样,在应该有图像的单元格中。在这一点上,我感到非常困惑,而且有点沮丧。
我的主要UIViewController
控制细胞:
class ExperimentalViewController: UIViewController
private let cellReuseId = "cellReuseId"
fileprivate var data = [(String, [UIImage])]()
fileprivate let strings = [<a large array of strings>]
private let collectionView: UICollectionView =
let layout = UICollectionViewFlowLayout()
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = .clear
collection.alwaysBounceVertical = true
collection.contentInsetAdjustmentBehavior = .always
return collection
()
override func viewDidLoad()
super.viewDidLoad()
view.backgroundColor = .white
setupDummyData()
setupCollectionView()
private func setupDummyData()
for (i, str) in strings.enumerated()
var images = [UIImage]()
if i % 2 == 0
images = [UIImage(named: "boxed-water-is-better-1464052-unsplash")!, UIImage(named: "boxed-water-is-better-1464052-unsplash")!, UIImage(named: "boxed-water-is-better-1464052-unsplash")!]
data.append((str, images))
private func setupCollectionView()
if #available(ios 13.0, *)
let size = NSCollectionLayoutSize(
widthDimension: NSCollectionLayoutDimension.fractionalWidth(1),
heightDimension: NSCollectionLayoutDimension.estimated(440)
)
let item = NSCollectionLayoutItem(layoutSize: size)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: size, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8)
section.interGroupSpacing = 5
let layout = UICollectionViewCompositionalLayout(section: section)
collectionView.collectionViewLayout = layout
else
let layout = ExperimentalFlowLayout()
collectionView.collectionViewLayout = layout
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(ExperimentalCell.self, forCellWithReuseIdentifier: cellReuseId)
view.addSubview(collectionView)
collectionView.anchor(top: view.topAnchor, leading: view.leadingAnchor, bottom: view.bottomAnchor, trailing: view.trailingAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
extension ExperimentalViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return data.count
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellReuseId, for: indexPath) as! ExperimentalCell
cell.index = indexPath
cell.backgroundColor = .yellow
cell.setupViews(withDatasource: data[indexPath.item])
return cell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat
return 20
我的UICollectionViewCell
子类
class ExperimentalCell: UICollectionViewCell
// https://***.com/questions/37782659/swift-ios-uicollectionview-images-mixed-up-after-fast-scroll/37784212
// http://www.thomashanning.com/the-most-common-mistake-in-using-uitableview/
private let imageCellReuseId = "imageCellReuseId"
var index: IndexPath?
var images = [UIImage]()
private let titleLabel: UILabel =
let label = UILabel()
label.numberOfLines = 0
return label
()
private let label: UILabel =
let label = UILabel()
label.numberOfLines = 0
return label
()
let collectionView: UICollectionView =
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = .clear
return collection
()
override init(frame: CGRect)
super.init(frame: frame)
print("RYANLOG \(index?.row) INIT")
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
override func prepareForReuse()
super.prepareForReuse()
images = []
imageView.image = nil
collectionView.delegate = nil
collectionView.dataSource = nil
titleLabel.text = nil
label.text = nil
setupViews(withDatasource: nil)
func setupViews(withDatasource datasource: (String, [UIImage])?)
if datasource != nil
parseDatasource(datasource!)
contentView.addSubview(titleLabel)
titleLabel.anchor(top: contentView.topAnchor, leading: contentView.leadingAnchor, bottom: nil, trailing: contentView.trailingAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
contentView.addSubview(label)
if images.count > 0
titleLabel.text = "Images"
label.text = String(images.count)
label.anchor(top: titleLabel.bottomAnchor, leading: contentView.leadingAnchor, bottom: nil, trailing: contentView.trailingAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
setupCollectionView()
contentView.addSubview(collectionView)
collectionView.anchor(top: label.bottomAnchor, leading: contentView.leadingAnchor, bottom: contentView.bottomAnchor, trailing: contentView.trailingAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 200)
else
titleLabel.text = "No Images"
label.anchor(top: titleLabel.bottomAnchor, leading: contentView.leadingAnchor, bottom: contentView.bottomAnchor, trailing: contentView.trailingAnchor, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, width: 0, height: 0)
private func parseDatasource(_ datasource: (String, [UIImage]))
label.text = datasource.0
images = datasource.1
print("RYANLOG \(index?.row) Images:", images.count)
// MARK: - Used for self-sizing on <= iOS 12
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes
layoutIfNeeded()
let layoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
layoutAttributes.bounds.size = systemLayoutSizeFitting(UIView.layoutFittingCompressedSize, withHorizontalFittingPriority: .required, verticalFittingPriority: .defaultLow)
return layoutAttributes
extension ExperimentalCell: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout
fileprivate func setupCollectionView()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.register(ScrollableImageView.self, forCellWithReuseIdentifier: imageCellReuseId)
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
return images.count
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: imageCellReuseId, for: indexPath) as! ScrollableImageView
cell.imageView.image = images[indexPath.item]
return cell
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize
return CGSize(width: 200, height: 200)
请原谅使用print("RYANLOG ...)
s。我一直在使用它们进行调试,并认为它们可能有助于显示我认为可能是问题的一部分。
ScrollableImageView
是UIView
的子类。如果您认为查看此类中包含的代码会有所帮助,请告诉我!
非常感谢任何帮助!
展示有缺陷行为的 gif: https://imgur.com/a/btAXPca
【问题讨论】:
【参考方案1】:更新:我终于使用两个不同的重用标识符解决了这个问题:"cellRuseId"
和 "imageCellReuseId"
。我仍然不确定这是否是正确的解决方案,但是,现在我的 collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int)
看起来像这样:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
let reuseId = data[indexPath.item].1.count > 0 ? imageCellReuseId : cellReuseId
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseId, for: indexPath) as! ExperimentalCell
cell.index = indexPath
cell.backgroundColor = .yellow
cell.setupViews(withDatasource: data[indexPath.item])
return cell
我的收藏视图可以正确滚动并正确地重用单元格。我不会接受这个答案,因为它不是最优雅的(特别是如果您有同一个单元格的多个变体),所以如果有人有更好的答案,请给出回复!
【讨论】:
以上是关于如何修复滚动时从 UICollectionViewCell 中消失的图像的主要内容,如果未能解决你的问题,请参考以下文章
滚动 UICollectionView 时保留 UICollectionViewCell