Paging UICollectionView by cells, not screen

前端 未结 22 2130
予麋鹿
予麋鹿 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:45

    Horizontal Paging With Custom Page Width (Swift 4 & 5)

    Many solutions presented here result in some weird behaviour that doesn't feel like properly implemented paging.


    The solution presented in this tutorial, however, doesn't seem to have any issues. It just feels like a perfectly working paging algorithm. You can implement it in 5 simple steps:

    1. Add the following property to your type: private var indexOfCellBeforeDragging = 0
    2. Set the collectionView delegate like this: collectionView.delegate = self
    3. Add conformance to UICollectionViewDelegate via an extension: extension YourType: UICollectionViewDelegate { }
    4. Add the following method to the extension implementing the UICollectionViewDelegate conformance and set a value for pageWidth:

      func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
          let pageWidth = // The width your page should have (plus a possible margin)
          let proportionalOffset = collectionView.contentOffset.x / pageWidth
          indexOfCellBeforeDragging = Int(round(proportionalOffset))
      }
      
    5. Add the following method to the extension implementing the UICollectionViewDelegate conformance, set the same value for pageWidth (you may also store this value at a central place) and set a value for collectionViewItemCount:

      func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) {
          // Stop scrolling
          targetContentOffset.pointee = scrollView.contentOffset
      
          // Calculate conditions
          let pageWidth = // The width your page should have (plus a possible margin)
          let collectionViewItemCount = // The number of items in this section
          let proportionalOffset = collectionView.contentOffset.x / pageWidth
          let indexOfMajorCell = Int(round(proportionalOffset))
          let swipeVelocityThreshold: CGFloat = 0.5
          let hasEnoughVelocityToSlideToTheNextCell = indexOfCellBeforeDragging + 1 < collectionViewItemCount && velocity.x > swipeVelocityThreshold
          let hasEnoughVelocityToSlideToThePreviousCell = indexOfCellBeforeDragging - 1 >= 0 && velocity.x < -swipeVelocityThreshold
          let majorCellIsTheCellBeforeDragging = indexOfMajorCell == indexOfCellBeforeDragging
          let didUseSwipeToSkipCell = majorCellIsTheCellBeforeDragging && (hasEnoughVelocityToSlideToTheNextCell || hasEnoughVelocityToSlideToThePreviousCell)
      
          if didUseSwipeToSkipCell {
              // Animate so that swipe is just continued
              let snapToIndex = indexOfCellBeforeDragging + (hasEnoughVelocityToSlideToTheNextCell ? 1 : -1)
              let toValue = pageWidth * CGFloat(snapToIndex)
              UIView.animate(
                  withDuration: 0.3,
                  delay: 0,
                  usingSpringWithDamping: 1,
                  initialSpringVelocity: velocity.x,
                  options: .allowUserInteraction,
                  animations: {
                      scrollView.contentOffset = CGPoint(x: toValue, y: 0)
                      scrollView.layoutIfNeeded()
                  },
                  completion: nil
              )
          } else {
              // Pop back (against velocity)
              let indexPath = IndexPath(row: indexOfMajorCell, section: 0)
              collectionView.scrollToItem(at: indexPath, at: .left, animated: true)
          }
      }
      

提交回复
热议问题