【发布时间】:2019-07-21 18:35:12
【问题描述】:
在此处输入代码我正在创建一个需要在 UICollectionViewCell 中使用 UICollectionView 的应用程序。我已经成功地在单元格中添加了UICollectionView 及其所有委托方法,并且在初始加载时,一切都正确加载。然而,一旦用户尝试在主UICollectionView 中进一步向下滚动,包含内部UICollectionView 的单元格基本上就会失去他们的视野。需要注意的一个有趣细节是,只有当用户以非蜗牛速度滚动时,问题才会出现。如果滚动速度足够慢,单元格将正确加载。否则,单元格将变得畸形。
不幸的是,我处理这个问题已经有一段时间了,目前已经接近/一个多月了。我已经尝试了 StackOverflow、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://stackoverflow.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
【问题讨论】:
标签: ios swift xcode uicollectionview