-
Notifications
You must be signed in to change notification settings - Fork 249
Pan gesture: document that TotalX/TotalY semantics differ between iOS (cumulative) and Android (incremental) #3251
Description
Summary
The pan gesture documentation describes TotalX and TotalY properties without noting that their meaning differs between platforms. On iOS, these values are cumulative (relative to where the gesture started). On Android, they are incremental (relative to the previous event). This silent behavioral difference causes pan gesture implementations that work correctly on one platform to behave incorrectly on the other.
Why it matters
A developer testing on iOS will write code like:
pan.PanUpdated += (s, e) =>
{
if (e.StatusType == GestureStatus.Running)
{
image.TranslationX = e.TotalX; // works on iOS — cumulative from start
image.TranslationY = e.TotalY;
}
};This code produces smooth, correct movement on iOS. On Android, TotalX/TotalY represent the delta since the last event, so directly assigning them causes the view to jump to a tiny offset each frame instead of tracking the full pan distance — a clearly broken UX that only manifests on Android.
The inverse is also true: code written for Android (accumulating deltas manually) will double-count on iOS.
What should be documented
Add a platform behavior note to docs/fundamentals/gestures/pan.md explaining the coordinate difference and providing a normalized pattern:
| Platform | TotalX / TotalY semantics |
|---|---|
| iOS | Cumulative offset from the gesture start position |
| Android | Incremental offset from the previous event |
| Windows | Cumulative offset from the gesture start position |
To write cross-platform pan code, track the previous translation and accumulate deltas on Android, or use the cumulative values directly on iOS/Windows:
double _startTranslationX, _startTranslationY;
pan.PanUpdated += (s, e) =>
{
switch (e.StatusType)
{
case GestureStatus.Started:
_startTranslationX = image.TranslationX;
_startTranslationY = image.TranslationY;
break;
case GestureStatus.Running:
image.TranslationX = _startTranslationX + e.TotalX;
image.TranslationY = _startTranslationY + e.TotalY;
break;
case GestureStatus.Completed:
_startTranslationX = image.TranslationX;
_startTranslationY = image.TranslationY;
break;
}
};This pattern works correctly on all platforms because it captures the starting translation and applies TotalX/TotalY as an offset from that baseline.
Suggested location
docs/fundamentals/gestures/pan.md — in the PanGestureRecognizer section, near the TotalX/TotalY property description