Xamarin Pinch To Zoom和Pan Containers离开它们的边界

时间:2016-07-29 15:59:50

标签: ios xamarin.forms gesture

我有一个带有大量文字的标签,我想要启用缩放和平移手势识别器。我使用了这里的食谱,然后将它们互相嵌套。

https://developer.xamarin.com/guides/xamarin-forms/user-interface/gestures/pinch/

https://developer.xamarin.com/guides/xamarin-forms/user-interface/gestures/pan/

问题是,两个容器对象都允许您在顶层页面视图中的任何位置完全移动标签的正常范围(如下图所示)。

有关如何实施这些限制的任何想法?我确定它只是对容器代码中的数学设置了一些限制,但我还没有找到正确的改变。

正如您在这些图像中看到的那样,缩放到缩放容器(没有平移)和平移容器(没有缩放)都允许您更改控件,使其超出其范围。

初始布局:

Initial layout

仅限缩放

Pinch-To-Zoom only

仅平移

Panning Only

捏合和平移

Pinch and Pan

上面的链接有容器代码,但是这里是:

PinchToZoomContainer.cs

public class PinchToZoomContainer : ContentView
{
    // Pinch Gesture variables
    double currentScale = 1;
    double startScale = 1;
    double xOffset = 0;
    double yOffset = 0;


    public PinchToZoomContainer ()
    {
        var pinchGesture = new PinchGestureRecognizer ();
        pinchGesture.PinchUpdated += OnPinchUpdated;
        GestureRecognizers.Add (pinchGesture);

    }



    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;
            Content.AnchorX = 0;
            Content.AnchorY = 0;
        }
        if (e.Status == GestureStatus.Running) {
            // Calculate the scale factor to be applied.
            currentScale += (e.Scale - 1) * startScale;
            currentScale = Math.Max (1, currentScale);

            // 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;
        }
    }

PanContainer.cs

public class PanContainer : ContentView
{
    double startX, startY;
    double x, y;

    public PanContainer ()
    {
        // Set PanGestureRecognizer.TouchPoints to control the 
        // number of touch points needed to pan
        var panGesture = new PanGestureRecognizer ();
        panGesture.PanUpdated += OnPanUpdated;
        GestureRecognizers.Add (panGesture);
    }

    void OnPanUpdated (object sender, PanUpdatedEventArgs e)
    {
        switch (e.StatusType) {

        case GestureStatus.Started:
            startX = Content.TranslationX;
            startY = Content.TranslationY;
            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));// App.ScreenWidth));
            //Content.TranslationY = Math.Max (Math.Min (0, y + e.TotalY), -Math.Abs (Content.Height - App.ScreenHeight)); //App.ScreenHeight));    
            Content.TranslationX = startX + e.TotalX;
            Content.TranslationY = startY + e.TotalY;

            break;

        case GestureStatus.Completed:
            // Store the translation applied during the pan
            x = Content.TranslationX;
            y = Content.TranslationY;
            break;
        }
    }
}

我想,在PanContainer上,我的问题在于我必须注释掉这些内容:

            //Content.TranslationX = Math.Max (Math.Min (0, x + e.TotalX), -Math.Abs (Content.Width - App.ScreenWidth));// App.ScreenWidth));
            //Content.TranslationY = Math.Max (Math.Min (0, y + e.TotalY), -Math.Abs (Content.Height - App.ScreenHeight)); //App.ScreenHeight));    

我将这些更改为更简单的版本,因为我无法找到App.ScreenWidth或.ScreenHeight属性。

然而,收缩容器就像它最初在配方中一样,仍然超出界限。

2 个答案:

答案 0 :(得分:0)

这个答案很可能很晚才能满足您的需求,Chet ......但是,您可以简单地将整个内容包装在ScrollView中(您可以根据需要适当地定位和/或调整大小)。这应该按预期工作。

  <ScrollView Grid.Column="2" VerticalOptions="Start">
    <PanContainer>
      <PanContainer.Content>
        <Image x:Name="SomeImage" Aspect="AspectFit"  />
      </PanContainer.Content>
    </PanContainer>
  </ScrollView>

干杯! 麦克

答案 1 :(得分:0)

有一个IsClippedToBounds属性帮助我解决了这个问题。

例如:

<PanContainer IsClippedToBounds="true">
    <PanContainer.Content>
        <Image x:Name="SomeImage" />
    </PanContainer.Content>
</PanContainer>

要获得捏合和平移,您可以将夹点元素包裹在平移元素中,反之亦然,或者您可以使用夹点和平移类中的函数创建单个类。后者可能更好。

仅此一点可能无法完全按照您的预期工作,因为捏合和平移功能中的计算彼此无法识别,因此,如果您捏缩放,那么平移功能不知道它可以现在又进一步了。