【问题标题】:UIScrollView delegate methods not called when UICollectionViewCompositionalLayout is set设置 UICollectionViewCompositionalLayout 时未调用 UIScrollView 委托方法
【发布时间】:2019-11-10 17:21:30
【问题描述】:

我目前有一个使用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")
  }
}

【问题讨论】:

    标签: ios swift uicollectionview uicollectionviewlayout


    【解决方案1】:

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

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

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

    但如果你好奇并且想尝试一下,你可以使用这个“被黑”的 collectionView 类:

    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
                }
            }
        }
    }
    

    这会将您的委托设置为正交部分附带的所有内部滚动视图。你不应该在生产环境中使用它,因为不能保证 Apple 会保持集合视图的内部工作方式相同,所以这个 hack 将来可能不会起作用,另外你可能会因为在 UIKit 中使用私有 API 而被拒绝当您提交构建以供发布时。

    【讨论】:

    • 有没有生产环境安全的方法来做到这一点?
    • 我仍然不知道生产环境安全方式@SriVinayakChaitanyaEshwa
    【解决方案2】:

    您可能只想使用NSCollectionLayoutSectionvisibleItemsInvalidationHandler 回调,它的作用类似于每次滚动部分时都会调用的UIScrollViewDelegate

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

    【讨论】:

    • 我试图设置正确的页面控制页面并做到了这一点。 section.visibleItemsInvalidationHandler = { (visibleItems, point, env) -> self.pageControl.currentPage = visibleItems.last?.indexPath.row 中的无效 ?? 0}。如果滚动太快,会出现一些卡顿,否则,它会正确显示当前页面。
    • @ThetHtun 我会尝试将调整视图的代码放入主队列DispatchQueue.main.async { /*update view here*/ } 这通常有助于解决在更新视图中的某些元素时似乎发生的延迟。
    • 很遗憾,这不能替代scrollViewDidEndDecelerating。每次滚动时可见项目发生变化时,显然都会调用此闭包。我还发现它被多次调用,这很难处理。如果 Apple 不使用组合布局破坏这种委托功能,那就太好了。
    【解决方案3】:

    在@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
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-12
      相关资源
      最近更新 更多