在我的设置中,我有一个具有芯片样式的水平集合视图,在初始加载时,默认选择第一个芯片,并与该芯片相对应,进行 API 调用,并将其数据加载到正下方的垂直 UICollectionView 中。
点击 UICollectionView 中的任何芯片时,将启动 API 调用,并使用
scrollToItem(at: selectedChipIndexPath, at: .centerHorizontally , animated: true)
将选定的单元格滚动到 CollectionView 的中心。另外,我必须重新加载最后选择的和当前选择的索引路径以指示选择了新单元格。
如果用户点击芯片,查看下面加载的数据,然后点击下一个芯片,一切都会正常工作。
但是,如果用户快速点击连续的芯片,那么
scrollToItem(at: selectedChipIndexPath, at: .centerHorizontally , animated: true)
有时会出现滚动动画故障,从而导致不愉快的用户体验体验。
这是我的代码:
horizontalCollectionView.reloadItems(at: [lastSelectedIndexPath, selectedChipIndexPath])
// API Call
/*
This is an async call, which ones finishes informs the ViewController to the update it's vertical collection view with new data
*/
viewModel.fetchProductData(forPositon: selectedChipIndexPath)
DispatchQueue.main. asyncAfter ( .now() + .seconds(0.4)) {
horizontalCollectionView.scrollToItem(at: selectedChipIndexPath, at: .centerHorizontally , animated: true)
}
如果我不将滚动延迟几毫秒,那么集合视图的滚动动画就会开始出现非常严重的故障。
那么任何人都可以为我指出正确的方向,如何处理或排队多个
scrollToItem(at:at:animated:)
调用,以便我的 collectionView 滚动动画不会出现故障。
我确实尝试将
scrollToItem(at:at:animated:)
包装在批量更新中,但没有成功。
更新:
这是我的 UI 的主要外观
水平集合视图:对于产品类别
垂直集合视图:对于产品本身
在没有看到完整代码的情况下,问题可能是由您重复调用
.reloadItems(...)
引起的
如果您这样做只是为了更改所选单元格的外观,则没有必要。
相反,让集合视图跟踪选定的单元格(默认情况下),并让您的单元格根据其选定状态修改其自身的外观。
例如 - 如果我们以如下典型方式创建一个具有单个标签的单元格:
class SomeCell: UICollectionViewCell {
static let identifier: String = "SomeCell"
let theLabel: UILabel = {
let v = UILabel()
v.textAlignment = .center
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
theLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(theLabel)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
theLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 4.0),
theLabel.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
theLabel.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
theLabel.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -4.0),
])
// default "un-selected" appearance
contentView.layer.borderColor = UIColor(white: 0.9, alpha: 1.0).cgColor
contentView.layer.borderWidth = 1.0
}
override func layoutSubviews() {
contentView.layer.cornerRadius = contentView.bounds.height * 0.5
}
// the collection view will tell us whether the cell is selected or not
// so update the cell appearance here
override var isSelected: Bool {
didSet {
contentView.backgroundColor = isSelected ? UIColor(red: 0.5, green: 1.0, blue: 0.5, alpha: 1.0) : .systemBackground
contentView.layer.borderWidth = isSelected ? 0.0 : 1.0
}
}
}
使用上述单元格的具有水平集合视图的示例视图控制器 - 请注意,我正在创建 4 个示例标签“集”,因此我们有足够的单元格可以滚动:
class SomeViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
var collectionView: UICollectionView!
var sampleTags: [String] = [
"Pomogranate",
"Banana, Guava, Sapota",
"Oranges, Mosambi",
"Apple",
"Blueberry",
"Pineapple",
"Strawberry",
]
var tagsData: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
// let's create several sets of the sample tags
// so we have plenty of cells to scroll
for i in 1...4 {
sampleTags.forEach { s in
tagsData.append("\(i): \(s)")
}
}
let fl = UICollectionViewFlowLayout()
fl.scrollDirection = .horizontal
fl.estimatedItemSize = .init(width: 80.0, height: 40.0)
collectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
collectionView.heightAnchor.constraint(equalToConstant: 50.0),
])
collectionView.register(SomeCell.self, forCellWithReuseIdentifier: SomeCell.identifier)
collectionView.dataSource = self
collectionView.delegate = self
collectionView.layer.borderColor = UIColor.red.cgColor
collectionView.layer.borderWidth = 1.0
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return tagsData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let c = collectionView.dequeueReusableCell(withReuseIdentifier: SomeCell.identifier, for: indexPath) as! SomeCell
c.theLabel.text = tagsData[indexPath.item]
return c
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// scroll the selected cell to the center
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
DispatchQueue.main.async {
self.viewModel.fetchProductData(forPositon: indexPath)
}
}
}