【问题标题】:UICollectionView CompositionalLayout not calling UIScrollDelegateUICollectionView CompositionalLayout 不调用 UIScrollDelegate
【发布时间】:2020-01-10 01:50:58
【问题描述】:

这是我的问题:

1.一旦 UICollectionView 实现了 compositionalLayout,就不会调用 ScrollViewDelegate

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

2。 collectionView.decelerationRate = .fast 在实现 CompositionalLayout 时被忽略

我的理解是 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 个项目时,滚动委托方法永远不会被调用。

虽然我仍然可以滚动,但预期的行为应该是滚动委托方法确实被调用,因为我有垂直反弹。

当有大约 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
        
    }

Scroll 代表永远不会被调用。即使有 40 多个项目。

关于如何强制 UICollectionView 与其 ScrollViewDelegates 进行通信的任何想法?

【问题讨论】:

  • 如果你不让类委托访问 UIScrollView,那么拥有 UIScrollView 委托方法有什么意义?
  • 您能详细说明一下吗?我将如何让班级代表访问 UiScrollView? UICollectionViewDelegate 不包含 UIScrollViewDelegate 吗?
  • 搜索讨论 UIScrollView 的主题。
  • 为什么当我删除组合布局时它仍然有效?不确定我的问题是否描述得不够清楚,但我认为搜索讨论 UIScrollView 的主题不会解决这个问题。我怀疑这是 CompositionalLayout 的实现。
  • 你在哪里设置self.collectionView.delegate

标签: swift uicollectionview uiscrollview


【解决方案1】:

答案是使用 visibleItemsInvalidationHandler 关闭部分 (NSCollectionLayoutSection)。

每当事件导致动画时,此处理程序都会接收更新。它的操作类似于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
}

【讨论】:

  • 太棒了,迫不及待想试试。你刚刚恢复了我对构图布局的兴奋。谢谢
【解决方案2】:

代码sn-p:

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

【讨论】:

    【解决方案3】:

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

    以防其他人遇到同样的问题。这似乎是水平滚动组合布局组的问题。

    只有垂直滚动组会触发 UIScrollViewDelegates。

    不幸的是,这也意味着似乎 decelerationrate.fast 不能应用于水平滚动。它只会被忽略。

    重现问题的步骤和分析

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

    结论

    UIScrollViewDelegate 只与垂直滚动组交互。 水平滚动组不与集合视图的滚动代理通信。

    如果在水平滚动组中需要滚动委托方法,而不是使用嵌套 CollectionView 和 FlowLayout / 自定义布局的传统方法。

    备注

    如果有人可以向我指出我遗漏了什么,我将非常感激,直到那时这将作为我上述问题的答案。

    感谢所有评论的人。

    【讨论】:

      【解决方案4】:

      我找到了一种方便的方法来处理这个问题,你可以避免设置正交滚动并使用配置来代替:

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

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

      【讨论】:

        【解决方案5】:
        如果您垂直滚动 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组合布局中从水平滚动中搜索跟踪当前显示单元格的人有所帮助

        【讨论】:

          【解决方案6】:

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

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

          在这些委托方法中 - 您可以在特定部分即将显示单元格时做出反应以结束显示单元格

          【讨论】:

            猜你喜欢
            • 2020-05-23
            • 2021-10-05
            • 2013-10-22
            • 1970-01-01
            • 2017-06-21
            • 1970-01-01
            • 1970-01-01
            • 2017-08-24
            • 2012-09-22
            相关资源
            最近更新 更多