Skip to content

Instantly share code, notes, and snippets.

@vinhnx
Last active May 26, 2023 06:40
Show Gist options
  • Select an option

  • Save vinhnx/95fc8cf4c23a4a0fd72c618173b72d5f to your computer and use it in GitHub Desktop.

Select an option

Save vinhnx/95fc8cf4c23a4a0fd72c618173b72d5f to your computer and use it in GitHub Desktop.
UICollectionView snap onto cell when scrolling horizontally

// reference: https://stackoverflow.com/a/43637969/1477298

While originally I was using Objective-C, I since switched so Swift and the original accepted answer did not suffice.

I ended up creating a UICollectionViewLayout subclass which provides the best (imo) experience as opposed to the other functions which alter content offset or something similar when the user has stopped scrolling.

class SnappingCollectionViewLayout: UICollectionViewFlowLayout {

    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        guard let collectionView = collectionView else { return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) }

        var offsetAdjustment = CGFloat.greatestFiniteMagnitude
        let horizontalOffset = proposedContentOffset.x + collectionView.contentInset.left

        let targetRect = CGRect(x: proposedContentOffset.x, y: 0, width: collectionView.bounds.size.width, height: collectionView.bounds.size.height)

        let layoutAttributesArray = super.layoutAttributesForElements(in: targetRect)

        layoutAttributesArray?.forEach({ (layoutAttributes) in
            let itemOffset = layoutAttributes.frame.origin.x
            if fabsf(Float(itemOffset - horizontalOffset)) < fabsf(Float(offsetAdjustment)) {
                offsetAdjustment = itemOffset - horizontalOffset
            }
        })

        return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y)
    }
}

For the most native feeling deceleration with the current layout subclass, make sure to set the following:

collectionView?.decelerationRate = UIScrollViewDecelerationRateFast
@reni99
Copy link
Copy Markdown

reni99 commented Nov 16, 2017

How would I be able to detect to which cell the scrollview snapped?

@bcupp
Copy link
Copy Markdown

bcupp commented Nov 17, 2017

Hey there, thanks for the code example! I am having trouble figuring how to implement this on a UICollectionView I have implemented inside my viewController. Would you be able to point me in the right direction?

@rohitntsnewscorp
Copy link
Copy Markdown

It is not working in my case !!

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