Skip to content

Instantly share code, notes, and snippets.

@DamianSuess
Last active April 20, 2022 17:52
Show Gist options
  • Save DamianSuess/fa38a2f952ca5f589de2d3e9a441f370 to your computer and use it in GitHub Desktop.
Save DamianSuess/fa38a2f952ca5f589de2d3e9a441f370 to your computer and use it in GitHub Desktop.
Xamarin.Forms - Pinch and Zoom
<!--
Based on the article:
* https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/pinch
* https://github.com/xamarin/xamarin-forms-samples/tree/master/WorkingWithGestures/PinchGesture
-->
<StackLayout>
<Label Text="Xamarin Forms- Pinch Gesture in Android and UWP"
VerticalOptions="Center" HorizontalOptions="Center"
FontSize="Large"
TranslationX="0" TranslationY="50" />
<Image Source="nature.jpg"
HeightRequest="200" WidthRequest="200"
VerticalOptions="Center" HorizontalOptions="Center"
TranslationX="0" TranslationY="100">
<Image.GestureRecognizers>
<PinchGestureRecognizer PinchUpdated="OnPinchUpdated" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
/*
Based on the article:
* https://docs.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/gestures/pinch
* https://github.com/xamarin/xamarin-forms-samples/tree/master/WorkingWithGestures/PinchGesture
*/
void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
if (e.Status == GestureStatus.Started)
{
startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
if (e.Status == GestureStatus.Running)
{
currentScale += (e.Scale - 1) * startScale;
currentScale = Math.Max(1, currentScale);
// The ScaleOrigin is in relative coordinatesto the wrapped UI element.
double renderedX = Content.X + xOffset;
double deltaX = renderedX / Width;
double deltaWidth = Width / (Content.Width * startScale);
double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
double renderedY = Content.Y + yOffset;
double deltaY = renderedY / Height;
double deltaHeight = Height / (Content.Height * startScale);
double originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
// Calculate the transformed element pixel coordinates
double targetX = xOffset - (originX * Content.Width) * (currentScale - startScale);
double targetY = yOffset - (originY * Content.Height) * (currentScale - startScale);
// Apply translation based on the change in origin
Content.TranslationX = Math.Min(0, Math.Max(targetX, -Content.Width * (currentScale - 1)));
Content.TranslationY = Math.Min(0, Math.Max(targetY, -Content.Height * (currentScale - 1)));
// Apply scale factor
Content.Scale = currentScale;
}
if (e.Status == GestureStatus.Completed)
{ // Store the deltas of the wrapped UI element
xOffset = Content.TranslationX;
yOffset = Content.TranslationY;
}
}
@tlhintoq
Copy link

Trying to use this in conjunction with other controls that take the Pan/scroll gesture seems to fail. ScrollView and ListView for example grab the Pinch gesture even though they themselves don't Pinch but they do scroll/pan. So it seems like Pinch inherits/uses that gesture so it is grabbed by the wrapped control. This seems to make Pinch a very niche feature that can only work on very static views of Label and Image for example, but nothing more complex.

In other words - any way to wrap a ScrollView or ListView with a PinchToZoom and have it work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment