设置UICollectionViewCompositionalLayout时未调用UIScrollView委托方法



我当前有一个使用UICollectionViewCompositionalLayoutUICollectionView。我想在滚动/滚动停止时在当前可见单元格中设置一些视图的动画。

不幸的是,将节上的orthogonalScrollingBehavior设置为除.none之外的任何内容似乎劫持了UICollectionView附带的UIScrollView委托方法。

想知道目前是否有解决办法要获取分页行为和UIScrollView委托吗

设置布局

enum Section {
case main
}
override func awakeFromNib() {
super.awakeFromNib()
collectionView.collectionViewLayout = createLayout()
collectionView.delegate = self
}
func configure() {
snapshot.appendSections([.main])
snapshot.appendItems(Array(0..<10))
dataSource.apply(snapshot, animatingDifferences: false)
}
private func createLayout() -> UICollectionViewLayout {
let leadingItem = NSCollectionLayoutItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0))
)
leadingItem.contentInsets = .zero
let containerGroup = NSCollectionLayoutGroup.horizontal(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1.0),
heightDimension: .fractionalHeight(1.0)
),
subitems: [leadingItem])
let section = NSCollectionLayoutSection(group: containerGroup)
section.orthogonalScrollingBehavior = .groupPaging // WOULD LIKE PAGING & UISCROLLVIEW TO ALSO BE FIRED
let config = UICollectionViewCompositionalLayoutConfiguration()
config.scrollDirection = .horizontal
let layout = UICollectionViewCompositionalLayout(section: section, configuration: config)
return layout
}

UICollectionViewDelegate

extension SlidingCardView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
// THIS IS FIRED BUT UISCROLLVIEW METHODS NOT
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
print(111)
}

func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
print("1111111")
}
}

orthogonalScrollingBehavior设置为节,嵌入一个内部_UICollectionViewOrthogonalScrollerEmbeddedScrollView,用于处理节中的滚动。此内部滚动视图将作为子视图添加到集合视图中。

当您将自己设置为收藏视图的delegate时,您应该会收到滚动视图委托回调,但仅限于主收藏视图,它在部分之间滚动,而不是在部分中的项目之间滚动。由于内部滚动视图(也可能是collectionViews,不确定(是完全不同的实例,并且您没有将自己设置为它们的委托,因此您不会收到它们的回调。

据我所知,不应该有一种官方的方式来接收来自内部滚动视图的这些回调,这些滚动视图处理分段滚动

但如果你很好奇,并且你想尝试一下,你可以使用这个"黑客入侵"的集合查看类:

import UIKit
final class OrtogonalScrollingCollectionView: UICollectionView {
override var delegate: UICollectionViewDelegate? {
get { super.delegate }
set {
super.delegate = newValue
subviews.forEach { (view) in
guard String(describing: type(of: view)) == "_UICollectionViewOrthogonalScrollerEmbeddedScrollView" else { return }
guard let scrollView = view as? UIScrollView else { return }
scrollView.delegate = newValue
}
}
}
}

这将把您的委托设置为所有带有正交部分的内部滚动视图。你不应该在生产环境中使用它,因为不能保证苹果会以相同的方式保持集合视图的内部工作,所以这种黑客攻击在未来可能不会奏效,而且当你提交发布版本时,你可能会因为在UIKit中使用私有API而被拒绝。

您可能只想使用NSCollectionLayoutSectionvisibleItemsInvalidationHandler回调,它就像UIScrollViewDelegate一样,每次部分滚动时都会调用它

let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .groupPagingCentered
section.visibleItemsInvalidationHandler = { (visibleItems, point, env) -> Void in
print(point)
}

这里有一个确定哪个单元格位于屏幕中心的解决方案:

section.visibleItemsInvalidationHandler = { [weak self] visibleItems, point, environment in
guard let self = self else { return }
for visibleCell in self.collectionView.visibleCells {
let collectionViewCenterPoint = self.collectionView.center

if let relativePoint = visibleCell.superview?.convert(collectionViewCenterPoint, from: nil),
visibleCell.frame.contains(relativePoint)
{
// visibleCell is in the center of the view.
} else {
// visibleCell is outside the center of the view.
}
}
}

根据@Stoyan的回答,我通过不寻找私有API来微调该类,使其与生产代码兼容。简单地看一下所有的UIScrollView子类。

此外,我认为最好在集合重载期间更新委托,因为在设置委托时可能还没有完整的视图层次结构。

最后,该类现在递归地查找UIScrollView,因此不会遗漏任何内容。

final class OrthogonalScrollingCollectionView: UICollectionView {
override func reloadData() {
super.reloadData()
scrollViews(in: self).forEach { scrollView in
scrollView.delegate = delegate
}
}
override func reloadSections(_ sections: IndexSet) {
super.reloadSections(sections)
scrollViews(in: self).forEach { scrollView in
scrollView.delegate = delegate
}
}
fileprivate func scrollViews(in subview: UIView) -> [UIScrollView] {
var scrollViews: [UIScrollView] = []
subview.subviews.forEach { view in
if let scrollView = view as? UIScrollView {
scrollViews.append(scrollView)
} else {
scrollViews.append(contentsOf: self.scrollViews(in: view))
}
}
return scrollViews
}
}

相关内容

最新更新