29
votes

I'm trying to implement a custom tab bar which is scrollable and has paging at each tab bar item. For that i'm using the delegate scrollViewWillEndDragging:withVelocity:targetContentOffset: which works perfectly with one problem.

The way my paging works is, if the contentOffset is near the right item, it the targetContentOffset changes to the offset of that item. Same thing for the left side.

The problem is, whenever I'm at at the left half of the first item and the right of the last (the scroll view works horizontally) it's supposed to go to ContentOffset 0 and the content offset of the rightmost item (minus the ones on screen), but it doesn't.

I checked with the debugger and the targetContentOffset->x is indeed 0 (in the first case - left of the leftmost item). So the problem is the UIScrollView not scrolling there. I'm lost.

Here is my implemented delegate:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                 withVelocity:(CGPoint)velocity
          targetContentOffset:(inout CGPoint *)targetContentOffset{
     NSInteger index = lrintf(targetContentOffset->x/self.tabWidth);
     targetContentOffset->x = index * self.tabWidth;
}

Here is the diagram explaining what i want to do.

|-------|-------|-------|-------|-------|-------|-------|
|       |       |       |       |       |       |       |
|       |       |       |       |       |       |       |
|_______|_______|_______|_______|_______|_______|_______|

        |_______________________________________|
         where it is and i scroll it to the left

   <----|

   |_______________________________________|
              where it would stop

|_______________________________________|
        where i want it to stop
5

5 Answers

15
votes

This is a known issue it seems. After some investigation and talking to other people it was suggested it could be a bug, which turned out to be correct. I reported it to Apple and was it marked as duplicate but is still open. Just answering for those of you with the same problem. I workaround it like Big Papoo suggests, by using an offset close to what I want (0.1 seems to do it). The same for the right end.

9
votes

Issue Fixed on iOS 6.0.

Now it works as it should.

8
votes

I tried setting the final offset to something slightly off zero or the content size as suggested by Big Papoo but noticed that this removes the over-scroll bounce. My solution to this was to check the original targetContentOffset to see if it is equal to zero or the contentSize and leaving it if it is:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

    float expectedOffset = targetContentOffset->x;
    if (expectedOffset == 0 || expectedOffset == scrollView.contentSize.width) return; // Scroll view will bounce so leave the targetContentOffset.

    float targetOffset = [self roundedOffset:expectedOffset]; // Round your offset.

    // Adjust the offset to make sure it works.
    if (targetOffset == 0) targetOffset = 1;
    else if (targetOffset == SCROLLVIEW_WIDTH) targetOffset = SCROLLVIEW_WIDTH - 1;

    targetContentOffset->x = targetOffset;
}

You can of course then use scrollViewDidEndDecelerating: to to move the scrollview that 1 point.

3
votes

I found 2 undocumented behaviors that you may investigate : - Setting zero as the final offset doesn't work, I suggest putting 1 or anything greater than zero (0.5 may work, not tested) - You'd better check the velocity sign and compute your final offset to be on the left or right side of the current point where the user removed is finger. Don't try to make the scrolling go backward on the other side.

0
votes

If you're using a horizontal table view, then inside your scrollViewWillEndDragging:withVelocity:targetContentOffset: function, you can call:

[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:<index> inSection:<section>] atScrollPosition:UITableViewScrollPositionTop animated:YES];

I found this to work much better than trying to manipulate targetContentOffset->y in a horizontal table view.