Skip to content

Instantly share code, notes, and snippets.

@jklp
Created July 29, 2014 06:30
Show Gist options
  • Save jklp/7d4201e3d9801ba78945 to your computer and use it in GitHub Desktop.
Save jklp/7d4201e3d9801ba78945 to your computer and use it in GitHub Desktop.
Snaps UICollectionViewCell to center when cell isn't entire width of screen
//
// Code based on http://markuzweb.blogspot.com.au/2014/02/uicollectionview-paging-for-smaller-width-cells.html
//
// Note, need to create property called "previousXoffset" in class and doesn't need to be
// initialized by default
//
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset {
CGPoint point = *targetContentOffset;
UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout *)self.collectionView.collectionViewLayout;
// This assumes that the values of `layout.sectionInset.left` and
// `layout.sectionInset.right` are the same with `layout.minimumInteritemSpacing`.
// Remember that we're trying to snap to one item at a time. So one
// visible item comprises of its width plus the left margin.
CGFloat visibleWidth = layout.minimumInteritemSpacing + layout.itemSize.width;
// It's either we go forwards or backwards, and only allowing user
// to scroll either 1 cell left or right (can't scroll more tha 2 cells)
int indexOfItemToSnap = round(self.previousXoffset / visibleWidth);
if (self.previousXoffset < point.x) {
indexOfItemToSnap++;
} else {
indexOfItemToSnap--;
}
// The only exemption is the last item.
CGFloat xOffset;
if (indexOfItemToSnap + 1 == [self.collectionView numberOfItemsInSection:0]) { // last item
xOffset = self.collectionView.contentSize.width - self.collectionView.bounds.size.width;
} else {
xOffset = indexOfItemToSnap * visibleWidth;
}
self.previousXoffset = xOffset;
*targetContentOffset = CGPointMake(xOffset, 0);
}
@jklp
Copy link
Author

jklp commented Jul 29, 2014

Note, snapping will only snap to the next cell and won't snap to later cells if you scroll faster.

If that functionality is needed, should just be a matter of looking at the velocity and adjusting as required.

@vikdenic
Copy link

Tried implementing this in Swift and no luck; notice anything off in my translation to Swift?

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {

    let point = targetContentOffset.pointee

    let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout

    let visibleWidth = layout.minimumInteritemSpacing + layout.itemSize.width

    var indexOfItemToSnap = round(self.previousXoffset / visibleWidth)

    if previousXoffset < point.x {
        indexOfItemToSnap += 1
    } else {
        indexOfItemToSnap -= 1
    }

    var xOffset = CGFloat()
    if indexOfItemToSnap + 1 == CGFloat(collectionView.numberOfItems(inSection: 0)) {
        xOffset = CGFloat(collectionView.contentSize.width - collectionView.bounds.size.width)
    } else {
        xOffset = indexOfItemToSnap * visibleWidth
    }
    previousXoffset = xOffset
    targetContentOffset.pointee = CGPoint(x: xOffset, y: 0)
}

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