swift UICollectionView合成布局未调用UIScrollDelegate

3ks5zfa0  于 2022-11-21  发布在  Swift
关注(0)|答案(7)|浏览(196)

我的问题是:

1.一旦UICollectionView实作compositionalLayout,就不会呼叫ScrollViewDelegate。

使用flowLayout和UICollectionViewDataSource调用scrollView委托。一旦我实现了可区分的数据源和CompositionalLayout,就不再调用scrollView委托了。

2.集合视图.减速率= .fast被忽略当实现复合布局时

我的理解是UICollectionViewDelegate应该调用UIScrollViewDelegate,我已经找了很远很远的地方,但是没有找到。有人能指出我遗漏了什么吗?我集成了compositionalLayout吗?
下面是我代码:

import UIKit

class ViewController: UIViewController, UICollectionViewDelegate, UIScrollViewDelegate {
    
    enum Section {
        case weekdayHeader
    }
    
    @IBOutlet weak var collectionView: UICollectionView!
    
    let weekdays = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday","Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
    
    var weekdaysArr = [Weekdays]()
    
    var dataSource: UICollectionViewDiffableDataSource<Section, Weekdays>! = nil
    
    private let cellReuseIdentifier = "myCell"
    
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView.delegate = self
        self.collectionView.register(UINib(nibName: "MyCollectionViewCell", bundle: nil), forCellWithReuseIdentifier: cellReuseIdentifier)
        self.collectionView.decelerationRate = .normal
        
        configureCollectionView()
        configureDataSource()
        configureWeekdays()
        updateCollectionView()
        
        // Do any additional setup after loading the view.
    }
    
    func configureCollectionView(){
        self.collectionView.delegate = self
        self.collectionView.collectionViewLayout = generateLayout()
    }
    
    func generateLayout() -> UICollectionViewLayout {
        
        let itemSize = NSCollectionLayoutSize(
            widthDimension: .estimated(10),
            heightDimension: .fractionalHeight(1))
        let weekdayItem = NSCollectionLayoutItem(layoutSize: itemSize)
        //        weekdayItem.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 50, bottom: 5, trailing: 50)
        
        let groupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.3),
            heightDimension: .fractionalHeight(1.0))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [weekdayItem])
        
        //        let spacing = CGFloat(10)
        //        group.interItemSpacing = .fixed(spacing)
        
        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = CGFloat(20)
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 500)
        section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
        
        
        
        
        let layout = UICollectionViewCompositionalLayout(section: section)
        
        return layout
        
    }
    
    func configureDataSource() {
        dataSource = UICollectionViewDiffableDataSource
            <Section, Weekdays>(collectionView: collectionView)
            { (collectionView: UICollectionView, indexPath: IndexPath, weekday: Weekdays) -> UICollectionViewCell? in
                guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: self.cellReuseIdentifier, for: indexPath) as? MyCollectionViewCell else {
                    fatalError("Cannot create a new cell") }
                cell.titleLabel.text = weekday.name
                print("weekday.name = \(weekday.name)")
                return cell
        }
        //      let snapshot = snapshotForCurrentState()
        //      dataSource.apply(snapshot, animatingDifferences: false)
    }
    
    func configureWeekdays(){
        
        weekdaysArr.append(Weekdays(name: "Monday"))
        weekdaysArr.append(Weekdays(name: "Tuesday"))
        weekdaysArr.append(Weekdays(name: "Wednesday"))
        weekdaysArr.append(Weekdays(name: "Thursday"))
        weekdaysArr.append(Weekdays(name: "Friday"))
        weekdaysArr.append(Weekdays(name: "Saturday"))
        weekdaysArr.append(Weekdays(name: "Sunday"))
        
    }
    
    func updateCollectionView(){
        var snapshot = NSDiffableDataSourceSnapshot<Section, Weekdays>()
        snapshot.appendSections([.weekdayHeader])
        snapshot.appendItems(weekdaysArr, toSection: .weekdayHeader)
        dataSource.apply(snapshot, animatingDifferences: false)
    }
    
    
    
    //MARK: Scroll View Delegate
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print("ScrollView decel rate: \(scrollView.decelerationRate)")
    }
    
    public func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        print("Begin")
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        print("End")
    }
    
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        print("END")
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
          
          print("Did select a cell here")
          
         }
    
    
}

struct Weekdays: Hashable {
    let identifier: UUID = UUID()
    let name: String
    
    func hash(into hasher: inout Hasher){
        return hasher.combine(identifier)
    }
    
    static func == (lhs: Weekdays, rhs: Weekdays) -> Bool {
        return lhs.identifier == rhs.identifier
    }
}

谢谢你
编辑:更新的代码-

编辑:与WWDC中的Apple示例代码进行比较,我发现了此行为。
如果我使用示例代码网格布局

func gridLayout() -> UICollectionViewLayout {
        let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(0.2),
                                             heightDimension: .fractionalHeight(1.0))
        let item = NSCollectionLayoutItem(layoutSize: itemSize)

        let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1.0),
                                              heightDimension: .fractionalWidth(0.2))
        let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize,
                                                         subitems: [item])

        let section = NSCollectionLayoutSection(group: group)

        let layout = UICollectionViewCompositionalLayout(section: section)
        return layout
    }

只有7个项目时,永远不会调用Scroll Delegate方法。
我仍然可以滚动,预期的行为应该是滚动代理方法在我有垂直反弹时被调用。
当有大约40个项目,但内容明显超出屏幕大小时,滚动代理确实会被调用。

有趣的是,现在加载我自己的布局时:

func generateLayout() -> UICollectionViewLayout {
        
        let itemSize = NSCollectionLayoutSize(
            widthDimension: .estimated(10),
            heightDimension: .fractionalHeight(1))
        let weekdayItem = NSCollectionLayoutItem(layoutSize: itemSize)
        //        weekdayItem.contentInsets = NSDirectionalEdgeInsets(top: 5, leading: 50, bottom: 5, trailing: 50)
        
        let groupSize = NSCollectionLayoutSize(
            widthDimension: .fractionalWidth(0.3),
            heightDimension: .fractionalHeight(1.0))
        let group = NSCollectionLayoutGroup.vertical(layoutSize: groupSize, subitems: [weekdayItem])
        
        //        let spacing = CGFloat(10)
        //        group.interItemSpacing = .fixed(spacing)
        
        let section = NSCollectionLayoutSection(group: group)
        section.interGroupSpacing = CGFloat(20)
        section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 500)
        section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
        
        
        
        
        let layout = UICollectionViewCompositionalLayout(section: section)
        
        return layout
        
    }

卷轴代理从来没有被调用。即使有40+项目。
关于如何强制UICollectionView与其ScrollViewDelegates进行通信,您有什么想法吗?

kknvjkwl

kknvjkwl1#

答案是在截面NSCollectionLayoutSection)上使用**visibleItemsInvalidationHandler**闭包。

每当事件产生动画时,此处理程序都会接收更新。它的操作类似于scrollViewDidScroll,并且在水平滚动组上,它将被传递所有项、滚动视图偏移量和NSCollectionLayoutEnvironment。
https://developer.apple.com/documentation/uikit/nscollectionlayoutsection/3199096-visibleitemsinvalidationhandler
示例:

section.visibleItemsInvalidationHandler = { [weak self] visibleItems, point, environment in
    self?.currentIndex = visibleItems.last?.indexPath.row
}
pu82cl6c

pu82cl6c2#

代码片段:

section.visibleItemsInvalidationHandler = { [weak self] visibleItems, point, environment in
    self?.pager.currentPage = visibleItems.last!.indexPath.row
}
nwlqm0z1

nwlqm0z13#

似乎是UICollectionView组合布局中水平滚动组的意外行为

以防其他人遇到同样的问题。这似乎是水平滚动组合布局组的问题。
只有垂直滚动组才会触发UIScrollViewDelegates。
不幸的是,这也意味着decelerationrate.fast似乎不能应用于水平滚动。它将被忽略。

重现问题和分析的步骤

通过在wwdc示例代码“集合视图布局中的改进”中实现UIScrollViewDelegate方法,可以重新创建此行为,如下所示:https://developer.apple.com/videos/play/wwdc2019/215/在类中:OrthogonalScrollBehaviorViewController.我们迅速找到水平和垂直滚动组。

结论

UIScrollViewDelegate只与垂直滚动组交互。水平滚动组不与集合视图的滚动委托通信。
如果在水平滚动组中需要滚动委托方法,则仍然需要具有嵌套CollectionViews和FlowLayout / Custom布局的传统方法。

备注

如果有人能指出我错过了什么,我会非常感激,直到那时,这将站在作为我的上述问题的答案。
感谢所有评论的人。

bt1cpqcv

bt1cpqcv4#

如果垂直滚动collectionView,UICollectionViewDelegate将调用scrollDidView。在我的情况下,我试图从水平滚动跟踪,我使用以下代码来使事情工作。

func collectionView(_ collectionView: UICollectionView, didEndDisplayingCell cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
      let visibleRect = CGRect(origin: yourCollectionView.contentOffset, size: photoCollectionView.bounds.size)
      let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
      let visibleIndexPath = yourCollectionView.indexPathForItem(at: visiblePoint) // Current display cell in collectionView
}

这不是此问题的答案,但希望这对在collectionView组合布局中从水平滚动搜索跟踪当前显示单元格的人有所帮助

koaltpgm

koaltpgm5#

我已经找到了一种处理此问题的简便方法,您可以避免设置正交滚动,而使用配置,方法如下:

let config = UICollectionViewCompositionalLayoutConfiguration()
config.scrollDirection = .horizontal
let layout = UICollectionViewCompositionalLayout(sectionProvider:sectionProvider,configuration: config)

这将调用collectionview的所有滚动代理。希望这对某些人有帮助。

xzlaal3s

xzlaal3s6#

我试过用

section.visibleItemsInvalidationHandler = { [weak self] visibleItems, point, environment in }

但是这个方法在滚动时被多次调用,所以对我来说最好的解决方案是使用时间戳,并检查时间戳的差异,只有当它大于某个值,比如1或2,才调用这个动作。

private var currentTimeStamp: TimeInterval = NSDate().timeIntervalSince1970 // private parameter to avoid multiple call on scroll, defined outside

section.visibleItemsInvalidationHandler = { [weak self] visibleItems, location, layoutEnvironment in
        guard let self = self else { return }
        let newTimeStamp = NSDate().timeIntervalSince1970
        let delta = newTimeStamp - self.currentTimeStamp
        if delta > 2 { 
            performAction()
        }
        self.currentTimeStamp = newTimeStamp
    }
kqhtkvqz

kqhtkvqz7#

根据您的用例,还可以使用集合视图委托方法

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)

func collectionView(_ collectionView: UICollectionView, didEndDisplayingCell cell: UICollectionViewCell, forItemAt indexPath: IndexPath)

在这些委托方法中,您可以在特定节将要显示单元格时做出React,以结束显示单元格

相关问题