Add snap-to position in a UITableView or UIScrollView
Solution 1
Like Pavan stated, scrollViewWillEndDragging: withVelocity: targetContentOffset: is the method you should use. It works with table views and scroll views. The code below should work for you if you are using a table view or vertically scrolling scroll view. 44.0 is the height of the table cells in the sample code so you will need to adjust that value to the height of your cells. If used for a horizontally scrolling scroll view, swap the y's for x's and change the 44.0 to the width of the individual divisions in the scroll view.
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
// Determine which table cell the scrolling will stop on.
CGFloat cellHeight = 44.0f;
NSInteger cellIndex = floor(targetContentOffset->y / cellHeight);
// Round to the next cell if the scrolling will stop over halfway to the next cell.
if ((targetContentOffset->y - (floor(targetContentOffset->y / cellHeight) * cellHeight)) > cellHeight) {
cellIndex++;
}
// Adjust stopping point to exact beginning of cell.
targetContentOffset->y = cellIndex * cellHeight;
}
Solution 2
I urge you to use the
scrollViewWillEndDragging: withVelocity: targetContentOffset:
method instead which is meant to be used for exactly your purpose. to set the target content offset to your desired position.
I also suggest you look at the duplicate questions already posted on SO.
Solution 3
If you really must do this manually, here is a Swift3 version. However, it's highly recommended to just turn on paging for the UITableView and this is handled for you already.
let cellHeight = 139
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
targetContentOffset.pointee.y = round(targetContentOffset.pointee.y / cellHeight) * cellHeight
}
// Or simply
self.tableView.isPagingEnabled = true
Solution 4
In Swift 2.0 when the table has a content inset and simplifying things, ninefifteen's great answer becomes:
func scrollViewWillEndDragging(scrollView: UIScrollView,
withVelocity velocity: CGPoint,
targetContentOffset: UnsafeMutablePointer<CGPoint>)
{
let cellHeight = 44
let y = targetContentOffset.memory.y + scrollView.contentInset.top + cellHeight / 2
var cellIndex = floor(y / cellHeight)
targetContentOffset.memory.y = cellIndex * cellHeight - scrollView.contentInset.top;
}
By just adding cellHeight / 2
, ninefifteen's if
-statement to increment the index is no longer needed.
Solution 5
Here's the Swift 2.0 equivalent
func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let cellWidth = CGRectGetWidth(frame) / 7 // 7 days
var index = round(targetContentOffset.memory.x / cellWidth)
targetContentOffset.memory.x = index * cellWidth
}
And this complicated rounding isn't necessary at all as long as you use round
instead of floor
Milo
Updated on June 17, 2022Comments
-
Milo almost 2 years
Is it possible to add a snap-to position in a UITableView or UIScrollView? What I mean is not auto scroll to a position if I press a button or call some method to do it, I mean is if I scroll my scroll view or tableview around a specific point, say
0, 30
, it will auto-snap to it and stay there? So if my scroll view or table view scrolls and then the user lets go inbetween0, 25
or0, 35
, it will auto "snap" and scroll there? I can imagine maybe putting in an if-statement to test if the position falls in that area in either thescrollViewDidEndDragging:WillDecelerate:
orscrollViewWillBeginDecelerating:
methods of UIScrollView but I'm unsure how to implement this in the case of a UITableView. Any guidance would be much appreciated. -
Pavan over 10 yearsCheers bro, I did try telling him, a few times. But I think he's persistent in his own approach.
-
Dave Y almost 4 yearsPerfect! Worked as a drop-in with minimal changes. Appreciate it.