Forum Xamarin Xamarin.Forms

How to use pinch and pan together with CarouselView

okprojectokproject USMember ✭✭
edited February 20 in Xamarin.Forms

Hi,

Preinfo

  • I created a product page with CarouselView using xamarin forms 4.x latest
  • CarouselView contains product images and you can swipe right or left to see images
  • I added pinch container to zoom in product

This part is nonproblematic.

Problem

  • i added pangesture to pinchContainer. Because users want to pan image detail after zoom in
  • But when i add pan gesture to pincContainer, CarouselView swipe feature is not working

Question: How to use pan and pinch together with CarouselView ?

What I ve tried ?

  • I can access CarouselView from pinchContainer onPan handler method - via object sender parameter
  • I handled if scale is bigger than 1, it means user using pinch so a little bit zoom in there. Then i can apply pan
  • If scale is 1, i want to use CarourselView default behaviour; but i could not find how to skip custom pan and trigger default CarouselView event after pan.
  • If you know how to skip pan and apply sender object pan behaviour, it will work for me

Thanks

Answers

  • JarvanJarvan Member, Xamarin Team Xamurai

    Try to create a custom Image class and add the pin gesture function in the this class. Then load the custom Image in the page.

    Page.xaml

    <StackLayout>
        <CarouselView x:Name="carouselView">
            <CarouselView.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <local:ZoomImage Source="{Binding Img}" HeightRequest="150"/>
                    </StackLayout>
                </DataTemplate>
            </CarouselView.ItemTemplate>
        </CarouselView>
    </StackLayout>
    

    Custom image class

    public class ZoomImage : Image
    {
        private const double MIN_SCALE = 1;
        private const double MAX_SCALE = 4;
        private const double OVERSHOOT = 0.15;
        private double StartScale;
        private double LastX, LastY;
    
        public ZoomImage()
        {
            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)
        {
            if (Scale > MIN_SCALE)
                switch (e.StatusType)
                {
                    case GestureStatus.Started:
                        LastX = TranslationX;
                        LastY = TranslationY;
                        break;
                    case GestureStatus.Running:
                        TranslationX = Clamp(LastX + e.TotalX * Scale, -Width / 2, Width / 2);
                        TranslationY = Clamp(LastY + e.TotalY * Scale, -Height / 2, Height / 2);
                        break;
                }
        }
    
        private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
        {
            switch (e.Status)
            {
                case GestureStatus.Started:
                    StartScale = Scale;
                    AnchorX = e.ScaleOrigin.X;
                    AnchorY = e.ScaleOrigin.Y;
                    break;
                case GestureStatus.Running:
                    double 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;
        }
    }
    

    Refer to: https://stackoverflow.com/a/53394537/11083277

  • okprojectokproject USMember ✭✭
    edited March 8

    Hi, thank you very much for your answer.
    I have some problems with this example.

    Is there any suggestion to solve problem ?

    Problem 1: I can use absolut layout attributes with Image tag; but i can not use them with ZoomImage. I need full screen image, so i use absolutelayout. How to make it full screen for all kind of device resolution, width, height etc..

    1. I can slide image if i use blurred are (image not placed there). Then i can also slide image via touching in image area.
      • But if i zoom in - zoom out for the image, i can not slide image event full zoom out there. I have to touch and slide blurred area first. So any solution for that ? Because i will use image full screen so there wont be area blurred; i mean all screen will contain the image; so i can not activate slide feature just using image area

  • okprojectokproject USMember ✭✭

    Any idea ?

  • JarvanJarvan Member, Xamarin Team Xamurai
    edited March 11

    How to make it full screen for all kind of device resolution, width, height etc..

    Sorry for the late reply. Try to display the image in the StackLayout and set the image's HorizontalOptions and VerticalOptions to FillAndExpand. Also set Aspect property to Fill.

    <StackLayout>
        <Image Source="img" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Aspect="Fill"/>
    </StackLayout>
    
  • okprojectokproject USMember ✭✭

    @Jarvan thank you very much again.
    Full height and width image problem solved.

    But the main problem still remain.

    So in the e-commerce field, most of mobile apps requires

    • Product detail page
    • Open image in full screen on mode on phone screen via touch
    • Slide images to navigate product images via swipe left -right
    • Zoom in - zoom out, pan product images to see details of product

    So this is basic most known use case but i still could not find a way to do it with Xamarin Forms. With Carousel View, it is possible to swipe images to navigate right or left, zoom in - zoom out images but it is not possible to pan images; because swipe used by carousel view; pan has interference with that.

    Is there anyone have an idea to solve that problem ?

Sign In or Register to comment.