Paging UICollectionView by cells, not screen

前端 未结 22 2139
予麋鹿
予麋鹿 2020-12-04 05:00

I have UICollectionView with horizontal scrolling and there are always 2 cells side-by-side per the entire screen. I need the scrolling to stop at the begining

22条回答
  •  不知归路
    2020-12-04 05:36

    Here is my way to do it by using a UICollectionViewFlowLayout to override the targetContentOffset:

    (Although in the end, I end up not using this and use UIPageViewController instead.)

    /**
     A UICollectionViewFlowLayout with...
     - paged horizontal scrolling
     - itemSize is the same as the collectionView bounds.size
     */
    class PagedFlowLayout: UICollectionViewFlowLayout {
    
      override init() {
        super.init()
        self.scrollDirection = .horizontal
        self.minimumLineSpacing = 8 // line spacing is the horizontal spacing in horizontal scrollDirection
        self.minimumInteritemSpacing = 0
        if #available(iOS 11.0, *) {
          self.sectionInsetReference = .fromSafeArea // for iPhone X
        }
      }
    
      required init?(coder aDecoder: NSCoder) {
        fatalError("not implemented")
      }
    
      // Note: Setting `minimumInteritemSpacing` here will be too late. Don't do it here.
      override func prepare() {
        super.prepare()
        guard let collectionView = collectionView else { return }
        collectionView.decelerationRate = UIScrollViewDecelerationRateFast // mostly you want it fast!
    
        let insetedBounds = UIEdgeInsetsInsetRect(collectionView.bounds, self.sectionInset)
        self.itemSize = insetedBounds.size
      }
    
      // Table: Possible cases of targetContentOffset calculation
      // -------------------------
      // start |          |
      // near  | velocity | end
      // page  |          | page
      // -------------------------
      //   0   | forward  |  1
      //   0   | still    |  0
      //   0   | backward |  0
      //   1   | forward  |  1
      //   1   | still    |  1
      //   1   | backward |  0
      // -------------------------
      override func targetContentOffset( //swiftlint:disable:this cyclomatic_complexity
        forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
    
        guard let collectionView = collectionView else { return proposedContentOffset }
    
        let pageWidth = itemSize.width + minimumLineSpacing
        let currentPage: CGFloat = collectionView.contentOffset.x / pageWidth
        let nearestPage: CGFloat = round(currentPage)
        let isNearPreviousPage = nearestPage < currentPage
    
        var pageDiff: CGFloat = 0
        let velocityThreshold: CGFloat = 0.5 // can customize this threshold
        if isNearPreviousPage {
          if velocity.x > velocityThreshold {
            pageDiff = 1
          }
        } else {
          if velocity.x < -velocityThreshold {
            pageDiff = -1
          }
        }
    
        let x = (nearestPage + pageDiff) * pageWidth
        let cappedX = max(0, x) // cap to avoid targeting beyond content
        //print("x:", x, "velocity:", velocity)
        return CGPoint(x: cappedX, y: proposedContentOffset.y)
      }
    
    }
    

提交回复
热议问题