title: Xamarin Forms pinch and pan together
body: <p>I have implemented both pan and pinch individually, and it works fine. I'm now trying to use pinch and pan together and I'm seeing some issues. Here's my code:</p>
  
  <p>XAML:</p>
  
  <pre class="lang-xaml prettyprint-override"><code>&lt;AbsoluteLayout x:Name="PinchZoomContainer"&gt;
    &lt;controls:NavBar x:Name="NavBar" ShowPrevNext="true" ShowMenu="false" IsModal="true" /&gt;
    &lt;controls:PanContainer  x:Name="PinchToZoomContainer"&gt;
      &lt;Image x:Name="ImageMain" /&gt;
    &lt;/controls:PanContainer&gt;
  &lt;/AbsoluteLayout&gt;
  </code></pre>
  
  <p>Pinch/Pan Gesture Add's:</p>
  
  <pre class="lang-cs prettyprint-override"><code>var panGesture = new PanGestureRecognizer();
  panGesture.PanUpdated += OnPanUpdated;
  GestureRecognizers.Add(panGesture);
  
  var pinchGesture = new PinchGestureRecognizer();
  pinchGesture.PinchUpdated += OnPinchUpdated;
  GestureRecognizers.Add(pinchGesture);
  </code></pre>
  
  <p>Pan Method:</p>
  
  <pre class="lang-cs prettyprint-override"><code>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:
              // Translate and ensure we don't pan beyond the wrapped user interface element bounds.
              Content.TranslationX = Math.Max(Math.Min(0, x + e.TotalX), -Math.Abs(Content.Width - App.ScreenWidth));
              Content.TranslationY = Math.Max(Math.Min(0, y + e.TotalY), -Math.Abs(Content.Height - App.ScreenHeight));
              break;
  
          case GestureStatus.Completed:
              // Store the translation applied during the pan
              x = Content.TranslationX;
              y = Content.TranslationY;
              break;
      }
  }
  </code></pre>
  
  <p>Pinch Method:</p>
  
  <pre class="lang-cs prettyprint-override"><code>void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
  {
      if (e.Status == GestureStatus.Started)
      {
          // Store the current scale factor applied to the wrapped user interface element,
          // and zero the components for the center point of the translate transform.
          startScale = Content.Scale;
          //ImageMain.AnchorX = 0;
          //ImageMain.AnchorY = 0;
      }
      if (e.Status == GestureStatus.Running)
      {
          // Calculate the scale factor to be applied.
          currentScale += (e.Scale - 1) * startScale;
          currentScale = Math.Max(1, currentScale);
          currentScale = Math.Min(currentScale, 2.5);
          // The ScaleOrigin is in relative coordinates to the wrapped user interface element,
          // so get the X pixel coordinate.
          double renderedX = Content.X + xOffset;
          double deltaX = renderedX / Width;
          double deltaWidth = Width / (Content.Width * startScale);
          double originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
  
          // The ScaleOrigin is in relative coordinates to the wrapped user interface element,
          // so get the Y pixel coordinate.
          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 = targetX.Clamp(-Content.Width * (currentScale - 1), 0);
          Content.TranslationY = targetY.Clamp(-Content.Height * (currentScale - 1), 0);
  
          // Apply scale factor
          Content.Scale = currentScale;
      }
      if (e.Status == GestureStatus.Completed)
      {
          // Store the translation delta's of the wrapped user interface element.
          xOffset = Content.TranslationX;
          yOffset = Content.TranslationY;
      }
  }
  </code></pre>
  
  <p>If I turn off either gesture and only use the other then the functionality works perfectly. The issue arises when I add the pan AND pinch gestures. What seems to be happening is this:</p>
  
  <p>1) The pan actually seems to be working as expected
  2) When you pan on the image initially, let's say, move the image to Y-center and X-center, and then you try to zoom, the image gets set back to it's initial state. Then, when you pan, it moves you back to where you were before you tried to zoom (which is why I say the pan is working fine).</p>
  
  <p>From what I'm understanding from my debugging is that when you zoom it's not taking into consideration the position you are currently at. So when you pan first, and then zoom, it doesn't zoom on the position you're at but the beginning point of the image. Then when you try to pan from there, the pan method still remembers where you were, and it moves you back to where you were before you tried to zoom.</p>
  
  <p>Hoping some insight on this. Obviously, there's an issue with my pinch method. I just think (obviously can't figure out) I need to add logic into it that takes into consideration where you're currently at. </p>