如何使用 Xamarin Forms 为 iOS 项目制作可点击的滑块?

How to make clickable slider for iOS project with Xamarin Forms?

我使用滑块来制作星级评分功能。为此,我将滑块放在背景中以接收手指的位置。这在 android 上工作得很好,因为每当你触摸滑块时它都会改变它的值。 iOS 上的 UISlider 仅在您触摸其拇指时触发事件。

我觉得,可以这样做。

1.Add 点击手势到整个滑块控件

        UILongPressGestureRecognizer uiTap = new UILongPressGestureRecognizer(Tapped);
        uiTap.MinimumPressDuration = 0;
        AddGestureRecognizer(uiTap);

2。从触摸读取坐标并将它们传递给控件。 但是我不知道该怎么做。

我的 xaml 看起来像这样。

<Grid>
    <Slider/>
    <Grid>
    <!-- here is my stars -->
    </Grid>
</Grid>

这里有很好的答案,代码在 objective-c:

好的,我能够将 swift 上的代码从 this answer 调整为 C# 代码,所以现在一切都按预期工作。

这是我的代码:

using MyProject.iOS.Renderers;
using CoreGraphics;
using Foundation;
using System;
using System.Linq;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ExportRenderer(typeof(Slider), typeof(ClickableSliderRenderer))]
namespace MyProject.iOS.Renderers
{
public class ClickableSliderRenderer : SliderRenderer
{
    UILongPressGestureRecognizer uiTap;
    protected override void OnElementChanged(ElementChangedEventArgs<Slider> e)
    {
        base.OnElementChanged(e);

        uiTap = new UILongPressGestureRecognizer(Tapped);
        uiTap.MinimumPressDuration = 0;
        AddGestureRecognizer(uiTap);

    }

    private void Tapped(object sender)
    {
        CGPoint point = uiTap.LocationInView(this);
        nfloat thumbWidth = Control.Subviews.LastOrDefault().Bounds.Size.Width;
        nfloat value;

        if (point.X <= thumbWidth / 2.0)
            value = Control.MinValue;
        else if (point.X >= Control.Bounds.Size.Width - thumbWidth / 2)
            value = Control.MaxValue;
        else
        {
            var percentage = ((point.X - thumbWidth / 2) / (Control.Bounds.Size.Width - thumbWidth));
            var delta = percentage * (Control.MaxValue - Control.MinValue);

            value = Control.MinValue + delta;
        }

        if (uiTap.State == UIGestureRecognizerState.Began)
        {
            UIView.Animate(0.35, delay: 0, options: UIViewAnimationOptions.CurveEaseInOut, animation:
                () =>
                {
                    Control.SetValue((float)value, true);
                    Control.SendActionForControlEvents(UIControlEvent.ValueChanged);
                }, completion: null);
        }
        else if (uiTap.State == UIGestureRecognizerState.Changed)
        {
            Control.SetValue((float)value, false);
            Control.SendActionForControlEvents(UIControlEvent.ValueChanged);
        }
        else
        {
            Control.SetValue((float)value, false);
        }

        Control.ThumbRectForBounds(Bounds, Bounds, (float)value);
    }
}
}

我在 Xamarin iOS C#

中创建了一个类似的解决方案

(改编自此answer

[Register(nameof(UISliderCustom))]
[DesignTimeVisible(true)]
public class UISliderCustom : UISlider
{

    public UISliderCustom(IntPtr handle) : base(handle) { }

    public UISliderCustom()
    {
        // Called when created from code.
        Initialize();
    }

    public override void AwakeFromNib()
    {
        // Called when loaded from xib or storyboard.
        Initialize();
    }

    void Initialize()
    {
        // Common initialization code here.

        var longPress = new UILongPressGestureRecognizer(tapAndSlide);
        longPress.MinimumPressDuration = 0;
        //longPress.CancelsTouchesInView = false;
        this.AddGestureRecognizer(longPress);
        this.UserInteractionEnabled = true;

    }

    private void tapAndSlide(UILongPressGestureRecognizer gesture)
    {
        System.Diagnostics.Debug.WriteLine($"{nameof(UISliderCustom)} RecognizerState {gesture.State}");

        // need to propagate events down the chain
        // I imagine iOS does something similar
        // for whatever recogniser on the thumb control
        // It's not enough to set CancelsTouchesInView because
        // if clicking on the track away from the thumb control
        // the thumb gesture recogniser won't pick it up anyway
        switch (gesture.State)
        {
            case UIGestureRecognizerState.Cancelled:
                this.SendActionForControlEvents(UIControlEvent.TouchCancel);
                break;

            case UIGestureRecognizerState.Began:
                this.SendActionForControlEvents(UIControlEvent.TouchDown);
                break;

            case UIGestureRecognizerState.Changed:
                this.SendActionForControlEvents(UIControlEvent.ValueChanged);                    
                break;

            case UIGestureRecognizerState.Ended:
                this.SendActionForControlEvents(UIControlEvent.TouchUpInside);
                break;

            case UIGestureRecognizerState.Failed:
                //?
                break;

            case UIGestureRecognizerState.Possible:
                //?
                break;

        }

        var pt = gesture.LocationInView(this);
        var thumbWidth = CurrentThumbImage.Size.Width;
        var value = 0f;

        if (pt.X <= thumbWidth / 2)
        {
            value = this.MinValue;
        }
        else if (pt.X >= this.Bounds.Size.Width - thumbWidth / 2)
        {
            value = this.MaxValue;
        }
        else
        {
            var percentage = ((pt.X - thumbWidth / 2) / (this.Bounds.Size.Width - thumbWidth));
            var delta = percentage * (this.MaxValue - this.MinValue);
            value = this.MinValue + (float)delta;
        }

        if (gesture.State == UIGestureRecognizerState.Began)
        {               
            UIView.Animate(0.35, 0, UIViewAnimationOptions.CurveEaseInOut,
                () =>
                {
                    this.SetValue(value, true);
                },
                null);
        }
        else
        {
            this.SetValue(value, animated: false);
        }

    }

}

如果您使用的是最近的稳定器 Xamarin.Forms Microsoft Documentation 有一个更简单的方法供您使用。基本上在 iOS 上为滑块设置一个 属性 特定命名空间。惊喜!

这是 Xaml 中的要点:

<ContentPage ...
         xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout ...>
    <Slider ... ios:Slider.UpdateOnTap="true" />
    ...
</StackLayout>

或在后面的代码中:

using Xamarin.Forms.PlatformConfiguration;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
...

//(in constructor or OnAppearing say)
var slider = new Xamarin.Forms.Slider();
slider.On<iOS>().SetUpdateOnTap(true);

希望对您有所帮助。