Reputation: 4173
I have used the code posted here by SebasianKruse https://forums.xamarin.com/discussion/74168/full-screen-image-viewer-with-pinch-to-zoom-pan-to-move-tap-to-show-captions-for-xamarin-forms/p2 to be able to pinch zoom and pan my image. It works great in Android, but in IOS I can't zoom nor pan.
I tried to set a breakpoint inside the OnPanUpdated
but its never reached on IOS.
here is my code:
xaml:
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0,0,1,1">
<Service:PinchToZoomContainer HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<ffimageloading:CachedImage RetryCount="5" RetryDelay="1" CacheDuration="1" x:Name="MyImage" HorizontalOptions="Fill" VerticalOptions="CenterAndExpand" DownsampleToViewSize="False">
</ffimageloading:CachedImage>
</Service:PinchToZoomContainer>
</StackLayout>
<StackLayout BindingContext="{x:Reference MyImage}" IsVisible="{Binding IsLoading}" Padding="12"
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1">
<ActivityIndicator BindingContext="{x:Reference MyImage}" IsRunning="{Binding IsLoading}" />
<Label Text="Loading Hi-Res Image..." BindingContext="{x:Reference MyImage}" IsVisible="{Binding IsLoading}" HorizontalOptions="Center" TextColor="Black"/>
</StackLayout>
</AbsoluteLayout>
</ContentPage.Content>
Service/PinchToZoomContainer.cs
using System;
using Xamarin.Forms;
using Xamarin.Forms.Internals;
namespace GalShare.Service
{
public class PinchToZoomContainer : ContentView
{
private double _startScale, _currentScale;
private double _startX, _startY;
private double _xOffset, _yOffset;
public double MinScale { get; set; } = 1;
public double MaxScale { get; set; } = 4;
public PinchToZoomContainer()
{
var tap = new TapGestureRecognizer { NumberOfTapsRequired = 2 };
tap.Tapped += OnTapped;
GestureRecognizers.Add(tap);
var pinchGesture = new PinchGestureRecognizer();
pinchGesture.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add(pinchGesture);
var pan = new PanGestureRecognizer();
pan.PanUpdated += OnPanUpdated;
GestureRecognizers.Add(pan);
}
protected override void OnSizeAllocated(double width, double height)
{
RestoreScaleValues();
Content.AnchorX = 0.5;
Content.AnchorY = 0.5;
base.OnSizeAllocated(width, height);
}
private void RestoreScaleValues()
{
Content.ScaleTo(MinScale, 250, Easing.CubicInOut);
Content.TranslateTo(0, 0, 250, Easing.CubicInOut);
_currentScale = MinScale;
_xOffset = Content.TranslationX = 0;
_yOffset = Content.TranslationY = 0;
}
private void OnTapped(object sender, EventArgs e)
{
if (Content.Scale > MinScale)
{
RestoreScaleValues();
}
else
{
//todo: Add tap position somehow
StartScaling();
ExecuteScaling(MaxScale, .5, .5);
EndGesture();
}
}
private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
switch (e.Status)
{
case GestureStatus.Started:
StartScaling();
break;
case GestureStatus.Running:
ExecuteScaling(e.Scale, e.ScaleOrigin.X, e.ScaleOrigin.Y);
break;
case GestureStatus.Completed:
EndGesture();
break;
}
}
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType)
{
case GestureStatus.Started:
_startX = e.TotalX;
_startY = e.TotalY;
Content.AnchorX = 0;
Content.AnchorY = 0;
break;
case GestureStatus.Running:
var maxTranslationX = Content.Scale * Content.Width - Content.Width;
Content.TranslationX = Math.Min(0, Math.Max(-maxTranslationX, _xOffset + e.TotalX - _startX));
var maxTranslationY = Content.Scale * Content.Height - Content.Height;
Content.TranslationY = Math.Min(0, Math.Max(-maxTranslationY, _yOffset + e.TotalY - _startY));
break;
case GestureStatus.Completed:
EndGesture();
break;
}
}
private void StartScaling()
{
_startScale = Content.Scale;
Content.AnchorX = 0;
Content.AnchorY = 0;
}
private void ExecuteScaling(double scale, double x, double y)
{
_currentScale += (scale - 1) * _startScale;
_currentScale = Math.Max(MinScale, _currentScale);
_currentScale = Math.Min(MaxScale, _currentScale);
var deltaX = (Content.X + _xOffset) / Width;
var deltaWidth = Width / (Content.Width * _startScale);
var originX = (x - deltaX) * deltaWidth;
var deltaY = (Content.Y + _yOffset) / Height;
var deltaHeight = Height / (Content.Height * _startScale);
var originY = (y - deltaY) * deltaHeight;
var targetX = _xOffset - (originX * Content.Width) * (_currentScale - _startScale);
var targetY = _yOffset - (originY * Content.Height) * (_currentScale - _startScale);
Content.TranslationX = targetX.Clamp(-Content.Width * (_currentScale - 1), 0);
Content.TranslationY = targetY.Clamp(-Content.Height * (_currentScale - 1), 0);
Content.Scale = _currentScale;
}
private void EndGesture()
{
_xOffset = Content.TranslationX;
_yOffset = Content.TranslationY;
}
}
}
Why does this code not work on IOS? according to the oter users posts in the forum above it should work on both systems..
Upvotes: 1
Views: 1089
Reputation: 6643
There won't be any difference even when testing it on a real physical device.
This is because Xcode 11.4 introduced a new protocol member to UIGestureRecognizerDelegate
and our initial proposed default value for ShouldReceiveEvent
is not playing well with the world.
Our product team has fixed this in Xamarin.iOS 13.16.0.13.
You could manually download and install the pkg on the Mac. But VS on Windows has released a new version now we can only temporarily develop it using VS for Mac after installing the new Xamarin iOS version.
Upvotes: 2