Search code examples
c#iosxamarin.formsuislider

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


I use slider to make rating stars feature. For this I put slider in background to recive position of finger. This works pretty fine on android, because whenever you touch slider it changes its value. UISlider on iOS fires events only when you touch its thumb.

I think, it could be done like this.

1.Add tap gesture to whole slider control

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

2. Read coordinates from touch and pass them to control. But I don't know how to do this.

My xaml looks like this.

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

Here good answers, with code on objective-c: https://stackoverflow.com/a/22982080/10139785


Solution

  • Okay, I was able to adapt code on swift from this answer into C# code, so everything works now as expected.

    Here is my code:

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