簡體   English   中英

Xamarin.Forms - 捕捉行為

[英]Xamarin.Forms - Snap behavior

幾年前,我在 Xamarin.iOS 中創建了一個類,它只是一個捕捉到屏幕邊緣的 UIView。

public class MovableUIButton : UIView
{
    private UIPanGestureRecognizer PanGesture;

    private UIButton btnShoppingCart;

    private UIDynamicAnimator animator;
    private PointF snapPoint;
    private bool isInitialized = false;
    private UISnapBehavior snap;

    // offsets used to position image relative to touch point while being dragged
    private float dx = 0;
    private float dy = 0;

    public event EventHandler TouchUpInside;

    public MovableUIButton(CGRect rect) : base(rect)
    {
        Initialize();
    }

    public MovableUIButton(IntPtr handle) : base(handle)
    {
        Initialize();
    }

    private void Initialize()
    {
        PanGesture = new UIPanGestureRecognizer(DidPan);
        this.AddGestureRecognizer(PanGesture);

        // Make it round
        this.Layer.CornerRadius = this.Bounds.Width / 2;
        this.BackgroundColor = UIColor.FromRGB(89f / 255f, 157f / 255f, 255f / 255f);

        // Set the Border
        this.Layer.BorderColor = UIColor.White.CGColor;
        this.Layer.BorderWidth = 3;

        // Set a Shadow
        this.Layer.ShadowColor = UIColor.White.CGColor;
        this.Layer.ShadowOpacity = .5f;
        this.Layer.ShadowRadius = 8.0f;
        this.Layer.ShadowOffset = new System.Drawing.SizeF(0f, 0f);

        btnShoppingCart = new UIButton(Bounds);
        btnShoppingCart.Font = FontAwesome.Font(30);
        btnShoppingCart.SetTitle(BaseFontAwesome.FAShoppingCart, UIControlState.Normal);
        btnShoppingCart.UserInteractionEnabled = true;
        btnShoppingCart.TouchUpInside += BtnShoppingCart_TouchUpInside;
        this.AddSubview(btnShoppingCart);
    }

    private void InitializeAnimator()
    {
        snapPoint = new PointF((float)SetX(Superview.Bounds), (float)SetY(Superview.Bounds));
        animator = new UIDynamicAnimator(Superview);
        isInitialized = true;
    }

    private void DidPan()
    {
        if (isInitialized == false)
        {
            InitializeAnimator();
        }

        if ((PanGesture.State == UIGestureRecognizerState.Began || PanGesture.State == UIGestureRecognizerState.Changed) && (PanGesture.NumberOfTouches == 1))
        {
            // remove any previosuly applied snap behavior to avoid a flicker that will occur if both the gesture and physics are operating on the view simultaneously
            if (snap != null)
                animator.RemoveBehavior(snap);

            var p0 = PanGesture.LocationInView(Superview);

            if (dx == 0)
                dx = (float)(p0.X - this.Center.X);

            if (dy == 0)
                dy = (float)(p0.Y - this.Center.Y);

            // this is where the offsets are applied so that the location of the image follows the point where the image is touched as it is dragged,
            // otherwise the center of the image would snap to the touch point at the start of the pan gesture
            var p1 = new PointF((float)(p0.X - dx), (float)(p0.Y - dy));

            this.Center = p1;
        }
        else if (PanGesture.State == UIGestureRecognizerState.Ended)
        {
            // reset offsets when dragging ends so that they will be recalculated for next touch and drag that occurs
            dx = 0;
            dy = 0;

            snapPoint = new PointF((float)SetX(Superview.Bounds), (float)SetY(Superview.Bounds));

            SnapImageIntoPlace((System.Drawing.PointF)PanGesture.LocationInView(Superview));
        }
    }

    void SnapImageIntoPlace(PointF touchPoint)
    {
        snap = new UISnapBehavior(this, snapPoint);
        animator.AddBehavior(snap);
    }

    private nfloat SetX(CGRect superBounds)
    {
        nfloat x = 0f;

        if (this.Center.X > superBounds.Width / 2)
        {
            x = superBounds.Width - 20;
        }
        else
        {
            x = 20;
        }

        return x;
    }

    private nfloat SetY(CGRect superBounds)
    {
        nfloat y = 0f;

        if (this.Center.Y < 20)
        {
            y = 20;
        }
        else if (this.Center.Y > superBounds.Height - 20)
        {
            y = superBounds.Height - 20;
        }
        else
        {
            return this.Center.Y;
        }

        return y;
    }

    public override void TouchesBegan(NSSet touches, UIEvent evt)
    {
        base.TouchesBegan(touches, evt);
    }

    private void BtnShoppingCart_TouchUpInside(object sender, EventArgs e)
    {
        TouchUpInside?.Invoke(this, null);
    }
}

此類包含Xamarin.Forms中不可用的UISnapBehavior對象。 因為我想將它移植到 Xamarin.Forms,是否有一個簡單的解決方法或類似於UISnapBehavior 的類?

不確定兩年后你是否還在等待這個問題的答案,但希望這對某人仍然有用。

您應該能夠獲取此類並將其放入您的 .iOS 項目中,並為您的 pcl 中的自定義控件創建自定義渲染器。 像這樣:

iOS 項目中的自定義渲染器:

[assembly: ExportRenderer(typeof(MovableUIButtonView), typeof(MovableButtonRenderer_iOS))]
namespace Test.iOS.Renderers
{
    public class MovableButtonRenderer_iOS : ViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<View> e)
        {
            if(Control == null)
                SetNativeControl(new MovableUIButton(Bounds));

            base.OnElementChanged(e);
        }
    }
}

要放入 pcl 的自定義控件:

namespace Test.Controls
{
    public class MovableUIButtonView : ContentView
    {
    }
}

您在任何頁面上使用的 XAML:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:local="clr-namespace:Test.Controls"
             mc:Ignorable="d"
             x:Class="Test.TestVW">

    <Grid>
        <local:MovableUIButtonView></local:MovableUIButtonView>
    </Grid>

</ContentPage>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM