3
votes

I want to create a functionality to tap and add a pin image over another background image and the background image should be able to zoom and pan this is the XAML code for this, here the pinch zoom is not working but tap event is working fine

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
         xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
         xmlns:local="clr-namespace:POC"
         xmlns:ui="clr-namespace:Vapolia.Lib.Ui;assembly=XamarinFormsGesture"
         x:Class="POC.MainPage"
         Title="Main Page">
<ScrollView AbsoluteLayout.LayoutFlags="All">
    <local:PinchAndPanContainer>
        <local:PinchAndPanContainer.Content >
            <AbsoluteLayout x:Name="AbsoluteLayoutForImage">
                <Image x:Name="FloorPlanImage" 
                Source="Capture2.png"
                HeightRequest="400"
                IsEnabled="True"
                InputTransparent="True"
                ui:Gesture.TapCommand2="{Binding TapCommand2}"/>//This Property
            </AbsoluteLayout>
        </local:PinchAndPanContainer.Content>
    </local:PinchAndPanContainer>
</ScrollView>

in the cs file, this tap command is adding a pin image inside the absolute layout using the coordinates in Point.

public Command<Point> TapCommand2 => new Command<Point>(point =>
    {
        AddPin(point);
    });

Now if we just remove ui:Gesture.TapCommand2="{Binding TapCommand2}" this property from the above code pinch and pan works fine.

For Tap event I used Vapolia.XamarinFormsGesture NuGet package and for pinch and pan used xamarin forms Gesture Recognizer Can anyone help

1
Hello Siddhant, welcome to StackOverflow just want to make sure about something are you trying to implement an image zoom functionality here?FreakyAli
Yes image zoom and pan functionalitySiddhant maurya
Use this and try adding the pointer to it and see if it works in case it does let me know and i will update my answerFreakyAli

1 Answers

1
votes

Recently I did a similar functionality and ended up creating a CustomControl as below:

Note: I have used the FFImageLoadings CachedImage in my class in case you are not using FFImage just replace it with your defualt xamarin forms image.

And this has the following functionalities: PanSwipe, Zoom and DoubleTap to zoom.

using System;
using Xamarin.Forms;
using FFImageLoading.Forms;

public class ZoomableImage : CachedImage //In case not using ff image replace this with the Image control
{
    private const double MIN_SCALE = 1;
    private const double MAX_SCALE = 4;
    private const double OVERSHOOT = 0.15;
    private double StartScale, LastScale;
    private double StartX, StartY;

    public ZoomableImage()
    {
        var pinch = new PinchGestureRecognizer();
        pinch.PinchUpdated += OnPinchUpdated;
        GestureRecognizers.Add(pinch);

        var pan = new PanGestureRecognizer();
        pan.PanUpdated += OnPanUpdated;
        GestureRecognizers.Add(pan);

        var tap = new TapGestureRecognizer { NumberOfTapsRequired = 2 };
        tap.Tapped += OnTapped;
        GestureRecognizers.Add(tap);

        Scale = MIN_SCALE;
        TranslationX = TranslationY = 0;
        AnchorX = AnchorY = 0;
    }

    protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
    {
        Scale = MIN_SCALE;
        TranslationX = TranslationY = 0;
        AnchorX = AnchorY = 0;
        return base.OnMeasure(widthConstraint, heightConstraint);
    }

    private void OnTapped(object sender, EventArgs e)
    {
        if (Scale > MIN_SCALE)
        {
            this.ScaleTo(MIN_SCALE, 250, Easing.CubicInOut);
            this.TranslateTo(0, 0, 250, Easing.CubicInOut);
        }
        else
        {
            AnchorX = AnchorY = 0.5; //TODO tapped position
            this.ScaleTo(MAX_SCALE, 250, Easing.CubicInOut);
        }
    }

    private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
    {
        switch (e.StatusType)
        {
            case GestureStatus.Started:
                StartX = (1 - AnchorX) * Width;
                StartY = (1 - AnchorY) * Height;
                break;
            case GestureStatus.Running:
                AnchorX = Clamp(1 - (StartX + e.TotalX) / Width, 0, 1);
                AnchorY = Clamp(1 - (StartY + e.TotalY) / Height, 0, 1);
                break;
        }
    }

    private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
    {
        switch (e.Status)
        {
            case GestureStatus.Started:
                LastScale = e.Scale;
                StartScale = Scale;
                AnchorX = e.ScaleOrigin.X;
                AnchorY = e.ScaleOrigin.Y;
                break;
            case GestureStatus.Running:
                if (e.Scale < 0 || Math.Abs(LastScale - e.Scale) > (LastScale * 1.3) - LastScale)
                { return; }
                LastScale = e.Scale;
                var current = Scale + (e.Scale - 1) * StartScale;
                Scale = Clamp(current, MIN_SCALE * (1 - OVERSHOOT), MAX_SCALE * (1 + OVERSHOOT));
                break;
            case GestureStatus.Completed:
                if (Scale > MAX_SCALE)
                    this.ScaleTo(MAX_SCALE, 250, Easing.SpringOut);
                else if (Scale < MIN_SCALE)
                    this.ScaleTo(MIN_SCALE, 250, Easing.SpringOut);
                break;
        }
    }

    private T Clamp<T>(T value, T minimum, T maximum) where T: IComparable
    {
        if (value.CompareTo(minimum) < 0)
            return minimum;
        else if (value.CompareTo(maximum) > 0)
            return maximum;
        else
            return value;
    }
}

Good luck, In case of queries kindly revert.