14

I'm facing with a simple but tedious problem. What I'm trying to do is make an UITableView to page like an UIScrollView but enabling paging doesn't help me so much because I can't set page size so the tableview scrolls exactly of its height so it shows rows 1...10 or 11...20 and so on. What I'd like instead is that no cell remains clipped above or under the view when I scroll (thus paging) without having a sort of fixed range of shown cells.

Thanks a lot

maxcanna
  • 1,755
  • 2
  • 12
  • 15

5 Answers5

18

More simple and more efficient :)

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{

    if(decelerate) return;

    [self scrollViewDidEndDecelerating:scrollView];
}


- (void)scrollViewDidEndDecelerating:(UITableView *)tableView {

    [tableView scrollToRowAtIndexPath:[tableView indexPathForRowAtPoint: CGPointMake(tableView.contentOffset.x, tableView.contentOffset.y+tableView.rowHeight/2)] atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
PRCode
  • 356
  • 2
  • 6
  • 2
    This solution worked perfectly for me, and worked for every scroll. The other solution was a little inconsistent with its results...sometimes it would snap to the top of the cell, other times it would not. – codeqi Jun 04 '13 at 12:23
  • @codeqi the reason it jumps to the top sometimes, is because you probably have a space between your items in the collectioView. In that case indexPathForRowAtPoint method will return 0,0 - which leads to the top. ;) – Lirik Mar 02 '14 at 10:39
13

Simple but efficient:

    - (void)scrollViewDidEndDecelerating:(UITableView *)tableView {
            int tomove = ((int)tableView.contentOffset.y%(int)tableView.rowHeight);
            if(tomove < tableView.rowHeight/2) [tableView setContentOffset:CGPointMake(0, tableView.contentOffset.y-tomove) animated:YES];
            else [tableView setContentOffset:CGPointMake(0, tableView.contentOffset.y+(tableView.rowHeight-tomove)) animated:YES];
    }

    - (void)scrollViewDidEndDragging:(UITableView *)scrollView willDecelerate:(BOOL)decelerate {
            if(decelerate) return;

            [self scrollViewDidEndDecelerating:scrollView];
    }
maxcanna
  • 1,755
  • 2
  • 12
  • 15
  • Doesn't really work for me. Plus, because you are using DidEndDecelerating, the code is not called when there is no deceleration (ie: if the table is not "flicked", but released gently) – pixelfreak Jul 01 '11 at 01:44
  • So you should add the same code to - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate if willDecelerate:(BOOL)decelerate is false – Julien Oct 18 '11 at 16:02
  • @Julien Yep, I forgot to paste the second method. Nobody noticed it for more than a year! Thx – maxcanna Oct 19 '11 at 08:10
  • 1
    My every 4th cell skipping. Pagination is working fine for 1st , 2nd & 3rd but as soon as I am trying to scroll4th it skip to 5th – Tariq Jul 05 '12 at 10:04
5

Starting with k06a's answer, I've refined it a bit so it works more like the real paginated UITableView. The differences in behaviour are quite noticeable with full screen table rows. Even a mini-flick in either direction should scroll the table to the next page: I do this by checking velocity first.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset
{    
    CGFloat rowHeight = tableView.rowHeight;
    int verticalOffset = ((int)targetContentOffset->y % (int)rowHeight);
    if (velocity.y < 0)
    {
        targetContentOffset->y -= verticalOffset;
    }
    else if (velocity.y > 0)
    {
        targetContentOffset->y += (rowHeight - verticalOffset);
    }
    // No velocity, snap to closest page
    else
    {
        if (verticalOffset < rowHeight / 2)
        {
            targetContentOffset->y -= verticalOffset;
        }
        else
        {
            targetContentOffset->y += (rowHeight - verticalOffset);
        }
    }    
}

Note that additionally setting

 self.tableView.decelerationRate = UIScrollViewDecelerationRateFast;

in viewDidLoad: makes it closer to the real thing, but not quite.

I've been fiddling with setting even faster deceleration rates using the code shown here but I couldn't get it right.

Community
  • 1
  • 1
Ricardo Sanchez-Saez
  • 9,466
  • 8
  • 53
  • 92
4

This works like real paging:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView 
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset
{
    int tomove = ((int)targetContentOffset->y % (int)self.tableView.rowHeight);
    if(tomove < self.tableView.rowHeight/2)
        targetContentOffset->y -= tomove;
    else 
        targetContentOffset->y += (self.tableView.rowHeight-tomove);
}

and make this in -viewDidLoad:

self.tableView.decelerationRate = UIScrollViewDecelerationRateFast;
k06a
  • 17,755
  • 10
  • 70
  • 110
  • This almost works perfectly, but fails for full screen sized cells when you do a very gentle flip (not enough of a flip for advancing the page). It instantly moves the cell back, instead of animating it smoothly. – Ricardo Sanchez-Saez Jul 08 '14 at 14:39
3

And in Swift...

func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    if !decelerate {
        self.scrollViewDidEndDecelerating(scrollView)
    }
}

func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    if let indexPathToScrollTo: NSIndexPath = self.tableView.indexPathForRowAtPoint(CGPointMake(self.tableView.contentOffset.x, self.tableView.contentOffset.y+tableView.rowHeight/2)) {
        self.tableView.scrollToRowAtIndexPath(indexPathToScrollTo, atScrollPosition: UITableViewScrollPosition.Top, animated: true)
    }
}
villy393
  • 2,985
  • 20
  • 28