views:

104

answers:

2

Note: The answer given here doesn't work for me.

I have a UIScrollView (not a table view, just a custom thing), and when the user takes certain actions, I want to kill any scrolling (dragging or deceleration) inside the view. I've tried doing e.g. this:

[scrollView scrollRectToVisible:CGRectInset([scrollView bounds], 10, 10) animated:NO];

on the theory that, given a rect that's already known visible, the scrolling will just stop where it is, but it turns out that this doesn't have any effect-- apparently the scroll view sees that the given rect is in bounds and takes no action. I can get the scroll to stop, if I give a rect that is definitely outside the currently-visible bounds, but inside the contentSize of the view. This seems to halt the view as expected... but also causes it to jump to some other location. I could probably do a little playing around at the margins to get this to work reasonably OK, but does anyone know of a clean way to halt a scroll view that's doing its thing?

Thanks.

A: 

The cleanest way will be subclassing UIScrollView and providing your own setContentOffset method. This should pass the message on, only if you haven't switched on your freeze boolean property.

Like so:

BOOL freeze; // and the @property, @synthesize lines..

-(void)setContentOffset:(CGPoint)offset
{
    if ( !freeze ) [super setContentOffset:offset];
}

Then, to freeze:

scrollView.freeze = YES;
mvds
Thanks. While this halts the visible scrolling, it doesn't actually stop the internal deceleration. If you toggle this on, and then off again after a moment, you'll see the scrolling pause, and then jump ahead and continue to decelerate. So the scrollview's internal state is still scrolling. Thus you need to freeze until you know deceleration has stopped, which you can do in partnership with a delegate, but it feels a little goofy to me. I'm hoping for something that actual just zaps the deceleration immediately (e.g. has the effect of a single tap on the screen).
quixoto
ok, these kind of issues sound familiar from prior experience... why not zap the deceleration by setting `decelerationRate = 1e10;` while freeze == YES?
mvds
(not knowing the internal math, 10*UIScrollViewDecelerationRateFast may be a wiser choice than 1e10)
mvds
Interesting idea! Unfortunately this value seems to be clamped. Tried it out, and there doesn't seem to be a value of this which will cause deceleration to immediately stop. Best I can do is make it sluggish. :)
quixoto
plan B is then to disable scrolling for a brief period - maybe you can get away with it, since you're blocking setContentOffset anyway, hopefully preventing side-effects from disabling scrolling.
mvds
Apparently the deceleration works multiplicatively. If you print out the deceleration rates they define, it gives `fast = 0.99`, and `normal = 0.998`. Even so though, apparently setting deceleration to 0 doesn't work, sadly.
David Liu
+3  A: 

I played with your original solution a bit, and this seems to work just fine. I think you almost had it, but you were just offsetting the rect that you used too much, and forgot that you could just scroll the rect straight back to the original rect.

The generalized solution for any scrolling action is this:

- (void)killScroll 
{
    CGPoint offset = scrollView.contentOffset;
    offset.x -= 1.0;
    offset.y -= 1.0;
    [scrollView setContentOffset:offset animated:NO];
    offset.x += 1.0;
    offset.y += 1.0;
    [scrollView setContentOffset:offset animated:NO];
}
David Liu
David: great, thanks. I had twiddled myself in this direction, but ended up with less trivial calculations about picking a 1x1 rect just outside the scroll bounds. Taking your suggestion of offsetting and then restoring immediately (which seems frankly to take advantage of an unpublished behavior where the successive calls in one event run actually work even though the "result" should be a no-op), it works fine. I'm going to edit your answer above to include the generalized solution that should work for any scroll direction. Thanks!
quixoto
@David: (Hope you don't mind the edits, wanted to clarify for later travelers. Thanks for your answer!)
quixoto