Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UIScrollView - snap to control when decelerating

The UIScrollView has a lot of information available to the programmer, but I dont see an obvious way to control the location that the control stop at after decelerating from a scroll gesture.

Basically I would like the scrollview to snap to specific regions of the screen. The user can still scroll like normal, but when they stop scrolling the view should snap to the most relevant location, and in the case of a flick gesture the deceleration should stop at these locations too.

Is there an easy way to do something like this, or should I consider the only way to accomplish this effect to write a custom scrolling control?

like image 930
Nippysaurus Avatar asked May 17 '11 22:05

Nippysaurus


1 Answers

Since the UITableView is a UIScrollView subclass, you could implement the UIScrollViewDelegate method:

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

And then compute what the closest desired target content offset is that you want, and set that on the inout CGPoint parameter.

I've just tried this and it works well.

First, retrieve the unguided offset like this:

CGFloat unguidedOffsetY = targetContentOffset->y;

Then Figure out through some math, where you'd want it to be, noting the height of the table header. Here's a sample in my code dealing with custom cells representing US States:

CGFloat guidedOffsetY;

if (unguidedOffsetY > kFirstStateTableViewOffsetHeight) {
    int remainder = lroundf(unguidedOffsetY) % lroundf(kStateTableCell_Height_Unrotated);
    log4Debug(@"Remainder: %d", remainder);
    if (remainder < (kStateTableCell_Height_Unrotated/2)) {
        guidedOffsetY = unguidedOffsetY - remainder;
    }
    else {
        guidedOffsetY = unguidedOffsetY - remainder + kStateTableCell_Height_Unrotated;
    }
}
else {
    guidedOffsetY = 0;  
}

targetContentOffset->y = guidedOffsetY;

The last line above, actually writes the value back into the inout parameter, which tells the scroll view that this is the y-offset you'd like it to snap to.

Finally, if you're dealing with a fetched results controller, and you want to know what just got snapped to, you can do something like this (in my example, the property "states" is the FRC for US States). I use that information to set a button title:

NSUInteger selectedStateIndexPosition = floorf((guidedOffsetY + kFirstStateTableViewOffsetHeight) / kStateTableCell_Height_Unrotated);
log4Debug(@"selectedStateIndexPosition: %d", selectedStateIndexPosition);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:selectedStateIndexPosition inSection:0];
CCState *selectedState = [self.states objectAtIndexPath:indexPath];
log4Debug(@"Selected State: %@", selectedState.name);
self.stateSelectionButton.titleLabel.text = selectedState.name;

OFF-TOPIC NOTE: As you probably guessed, the "log4Debug" statements are just logging. Incidentally, I'm using Lumberjack for that, but I prefer the command syntax from the old Log4Cocoa.

like image 178
idStar Avatar answered Oct 13 '22 06:10

idStar