Reputation: 1
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
Upvotes: 0
Views: 1024
Reputation: 11
if you're on the recent stable Xamarin.Forms Microsoft Documentation has an easier way for you. Basically set a property on iOS specific namespace for the slider. Surprise!
Here's the gist of it in Xaml:
<ContentPage ...
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core">
<StackLayout ...>
<Slider ... ios:Slider.UpdateOnTap="true" />
...
</StackLayout>
or in the code behind:
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);
Hope this helps.
Upvotes: 1
Reputation: 51
I have created a similar solution in Xamarin iOS C#
(Adapted from this 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);
}
}
}
Upvotes: 0
Reputation: 1
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);
}
}
}
Upvotes: 0