Paging UIScrollView in increments smaller than frame size
Data Structures & Algorithms practice on Codemia
Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.
Introduction
UIScrollView's built-in isPagingEnabled snaps to multiples of the scroll view's frame size. To page in smaller increments (such as showing a card carousel where adjacent cards peek from the edges), you must disable isPagingEnabled and implement custom snapping in the scroll view delegate's scrollViewWillEndDragging(_:withVelocity:targetContentOffset:) method. Alternatively, you can use a UICollectionView with UICollectionViewFlowLayout paging behavior or the modern UICollectionViewCompositionalLayout with orthogonal scrolling.
The Problem with Default Paging
With isPagingEnabled = true, the scroll view snaps in increments of 375 points (its frame width). There is no built-in property to change the paging increment.
Custom Paging with Delegate
Disable built-in paging and control snapping manually:
The key is scrollViewWillEndDragging(_:withVelocity:targetContentOffset:) — it lets you modify the target offset before the deceleration animation begins.
Clip-to-Bounds Trick for Peeking Cards
To show adjacent cards peeking from the edges while still using built-in paging:
The scroll view's frame is 295pt wide, so paging snaps in 295pt increments. Setting clipsToBounds = false makes adjacent cards visible beyond the frame edges.
UICollectionView with Custom Paging
UICollectionView provides more structured paging control:
Override targetContentOffset(forProposedContentOffset:withScrollingVelocity:) on the layout to control snapping behavior.
Common Pitfalls
- Leaving
isPagingEnabled = truewith custom snapping: IfisPagingEnabledis true, the scroll view's built-in paging overrides the delegate'stargetContentOffset. Always setisPagingEnabled = falsewhen implementing custom paging logic. - Forgetting
decelerationRate = .fast: Without fast deceleration, the scroll view glides too far after a flick, making custom snapping feel sluggish. Set.fastto match the feel of native paging. - Not handling velocity in the delegate: Without velocity checks, a fast flick may snap to the current page instead of advancing. Check
velocity.xto determine whether the user intended to advance or retreat. - Setting
clipsToBounds = falsewithout handling touches: WhenclipsToBounds = false, content is visible outside the scroll view's frame but taps on that content are not registered. OverridehitTest(_:with:)on the parent view to forward touches to the scroll view. - Incorrect
contentSizecalculation: IfcontentSizedoes not account for spacing between pages, the last page may not be reachable or may snap to the wrong position. Calculate as(pageWidth + spacing) * pageCount.
Summary
isPagingEnabledonly supports paging at the scroll view's frame width — no built-in way to change the increment- Implement custom paging by disabling
isPagingEnabledand usingscrollViewWillEndDragging(_:withVelocity:targetContentOffset:)to snap to custom increments - Use the
clipsToBounds = falsetrick to show peeking adjacent cards with built-in paging on a smaller frame - For collection views, override
targetContentOffset(forProposedContentOffset:withScrollingVelocity:)on a custom flow layout - Always set
decelerationRate = .fastand handle velocity for natural-feeling paging - Forward touches to the scroll view when using
clipsToBounds = falseto keep off-frame content interactive
Related reading
- PANIC Broken AVD system path. Check your ANDROID_SDK_ROOT value
- Parcelable encountered IOException writing serializable object getactivity
- Parse for iOS Errors when trying to run the app
- Parsing HTML into NSAttributedText - how to set font?
- Parsing query strings on Android
- Parsing query strings on Android
- Pass deep link into iOS Simulator?
- Passing an array to a function with variable number of args in Swift
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseNo course covers this one yet
Basis Lab writes one for you from a sentence about what you want to be able to do, then teaches it and checks you understood. Your first course is free.
Build my courseData Structures & Algorithms practice on Codemia
Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.