I'm using Xamarin to develop an iOS app and I want to subclass UIScrollView in order to handle the pan gesture in the scroll view based on its velocity. So, I made an override of GestureRecognizerShouldBegin
and I check the VelocityInView
of the pan gesture. This works fine for the first gesture, but subsequent pan gestures that fire while the scroll view is in motion (decelerating) always report a velocity of (0, 0):
public class MyScroll : UIScrollView
{
public override bool GestureRecognizerShouldBegin(UIGestureRecognizer gestureRecognizer)
{
UIPanGestureRecognizer panGesture = gestureRecognizer as UIPanGestureRecognizer;
if (panGesture != null)
{
CGPoint velocity = panGesture.VelocityInView(this);
Console.WriteLine("Pan gesture velocity: " + velocity);
}
return true;
}
}
Output after panning once and then a second time while the scroll is in motion:
Pan gesture velocity: {X=37.92359, Y=-872.2426}
Pan gesture velocity: {X=0, Y=0}
Is this a bug or is this the expected behavior?
Edit: cross-posted on Xamarin's forum: https://forums.xamarin.com/discussion/54478/uiscrollview-pan-gesture-velocity-reporting-0-if-it-is-already-moving#latest
Edit to clarify:
To clarify what I'm ultimately trying to do: I have a vertical scroll view inside a horizontal paging view. I want to check the velocity of the pan so that I can tell the scroll view to not handle that gesture if the pan is "horizontal" (i.e., X velocity > Y velocity). The default behavior is such that once the scroll view is in motion, another gesture still scrolls, but this makes it difficult for users to scroll horizontally (across pages) until the vertical scroll has completely settled.
I finally figured it out. Thanks to @RobertN for his assistance :)
The key is that the default pan gesture recognizer used by the scroll view will always report 0 velocity if it is already in motion (e.g., the inertia from a previous gesture is still in effect). Adding a new
UIPanGestureRecognizer
is a good way to record the "actual" velocity of a subsequent gesture, but by that time it is too late to affect the original pan gesture'sGestureRecognizerShouldBegin
. So all I have to do is add aShouldBegin
delegate to my newUIPanGestureRecognizer
and use that to returnfalse
in the case where I want the gesture to "fall through" to the parent pager.This way, I just let the default scroll view pan gesture recognizer do its thing, while my new
UIPanGestureRecognizer
recognizes when the user is making a new horizontal gesture, and passes that one through so that the pager can page. This makes the combination of the parent pager and vertical page scroll views operate nicely together (imagine having vertically scrolling pages and being able to flip through pages, even if the vertical page is in motion). Note, you also need to implement the following method to allow both gesture recognizers to operate simultaneously:In terms of grabbing the p/s via VelocityInView on GestureRecognizerShouldBegin, getting 0,0 after a pan motion has started, but not stopped/reset, is expected, at least in my experience. Obj-C/Swift is going to return the same thing, do not ask me why, have to get an actual iOS dev to ask the reason on that one.
Grabbing the velocity 'anywhere' else and you should be golden, if you really need within
GestureRecognizerShouldBegin
assign a private CGPoint within your UIScrollView sub-class from any other pan recognizer (I do that in the example below)...Example output:
Example UIScrollView subclass:
Note: This uses
shouldRecognizeSimultaneouslyWithGestureRecognizer
in order to allow auto-panning to continue after the user lifts their touchNote2: Not sure if I captured all the gesture state permutations, so adjust as needed