Skip to content

Instantly share code, notes, and snippets.

@mmick66
Last active August 2, 2022 10:06
Show Gist options
  • Star 88 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save mmick66/9812223 to your computer and use it in GitHub Desktop.
Save mmick66/9812223 to your computer and use it in GitHub Desktop.
UICollectionViewFlowLayout with arbitrary sized Paging
#import "UICollectionViewFlowLayoutCenterItem.h"
@implementation UICollectionViewFlowLayoutCenterItem
- (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity
{
CGSize collectionViewSize = self.collectionView.bounds.size;
CGFloat proposedContentOffsetCenterX = proposedContentOffset.x + self.collectionView.bounds.size.width * 0.5f;
CGRect proposedRect = self.collectionView.bounds;
// Comment out if you want the collectionview simply stop at the center of an item while scrolling freely
// proposedRect = CGRectMake(proposedContentOffset.x, 0.0, collectionViewSize.width, collectionViewSize.height);
UICollectionViewLayoutAttributes* candidateAttributes;
for (UICollectionViewLayoutAttributes* attributes in [self layoutAttributesForElementsInRect:proposedRect])
{
// == Skip comparison with non-cell items (headers and footers) == //
if (attributes.representedElementCategory != UICollectionElementCategoryCell)
{
continue;
}
// == First time in the loop == //
if(!candidateAttributes)
{
candidateAttributes = attributes;
continue;
}
if (fabsf(attributes.center.x - proposedContentOffsetCenterX) < fabsf(candidateAttributes.center.x - proposedContentOffsetCenterX))
{
candidateAttributes = attributes;
}
}
return CGPointMake(candidateAttributes.center.x - self.collectionView.bounds.size.width * 0.5f, proposedContentOffset.y);
}
@end
@romanfurman6
Copy link

Maybe some one need

  override func layoutSubviews() {
    super.layoutSubviews()
    if
      let first = collectionView.layoutAttributesForItem(at: IndexPath(item: 0, section: 0)),
      let last = collectionView.layoutAttributesForItem(at: IndexPath(item: collectionView.numberOfItems(inSection: 0) - 1, section: 0)) {

      let left = collectionView.frame.width / 2 - first.bounds.size.width / 2
      let right = collectionView.frame.width / 2  - last.bounds.size.width / 2
      collectionView.contentInset = UIEdgeInsets(top: 0.0, left: left, bottom: 0.0, right: right)
    }
    self.collectionView.decelerationRate = UIScrollViewDecelerationRateFast
  }
class CenterCellCollectionViewFlowLayout: UICollectionViewFlowLayout {

  override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {

    let collectionViewSize = self.collectionView!.bounds.size
    let proposedContentOffsetCenterX = proposedContentOffset.x + collectionViewSize.width * 0.5

    var proposedRect = self.collectionView!.bounds

    // comment this out if you don't want it to scroll so quickly
    proposedRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionViewSize.width, height: collectionViewSize.height)

    var candidateAttributes: UICollectionViewLayoutAttributes?
    for attributes in self.layoutAttributesForElements(in: proposedRect)! {
      // == Skip comparison with non-cell items (headers and footers) == //
      if attributes.representedElementCategory != .cell {
        continue
      }

      // Get collectionView current scroll position
      let currentOffset = self.collectionView!.contentOffset

      // Don't even bother with items on opposite direction
      // You'll get at least one, or else the fallback got your back
      // swiftlint:disable:next line_length
      if (attributes.center.x <= (currentOffset.x + collectionViewSize.width * 0.5) && velocity.x > 0) || (attributes.center.x >= (currentOffset.x + collectionViewSize.width * 0.5) && velocity.x < 0) {

        continue
      }

      // First good item in the loop
      if candidateAttributes == nil {
        candidateAttributes = attributes
        continue
      }

      // Save constants to improve readability
      let lastCenterOffset = candidateAttributes!.center.x - proposedContentOffsetCenterX
      let centerOffset = attributes.center.x - proposedContentOffsetCenterX

      if fabsf( Float(centerOffset) ) < fabsf( Float(lastCenterOffset) ) {
        candidateAttributes = attributes
      }
    }

    if candidateAttributes != nil {
      // Great, we have a candidate
      return CGPoint(x: candidateAttributes!.center.x - collectionViewSize.width * 0.5, y: proposedContentOffset.y)
    } else {
      // Fallback
      return super.targetContentOffset(forProposedContentOffset: proposedContentOffset)
    }
  }

}

@Iraniya
Copy link

Iraniya commented Mar 22, 2018

@DavidSchechter did you found the solution for scrolling one cell at a time using this? or anyone else?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment