The goal was to derive from a UIImageView and trigger an Activate event when a specified slider image got within range of the right end of the control. I found this was fairly straightforward to implement using a pan gesture. Below is the class I came up with and the associated images that were used:
Image
Slider
public class UISlideToActivateImageView : UIImageView
{
#region Fields and Properties
public event EventHandler<EventArgs> Activate;
protected const float DEFAULT_ACTIVATION_RANGE = 20;
public float ActivationRange { get; set; }
public static Selector PanSelector
{
get
{
return new Selector("HandlePan");
}
}
private UIImageView _sliderView = null;
public UIImage Slider
{
get
{
return _sliderView.Image;
}
set
{
if (value != null)
{
if (_sliderView != null)
{
_sliderView.RemoveFromSuperview();
}
_sliderView = new UIImageView(
new RectangleF(new PointF(0, 0), value.Size));
_sliderView.Image = value;
AddSubview(_sliderView);
}
}
}
protected PointF InitialLocation { get; set; }
#endregion
#region Constructors
public UISlideToActivateImageView(PointF location, UIImage image)
: base(image)
{
ActivationRange = DEFAULT_ACTIVATION_RANGE;
Frame = new RectangleF(location, image.Size);
RegisterPanGesture();
}
public UISlideToActivateImageView(PointF location, UIImage image,
UIImage slider) : base(image)
{
ActivationRange = DEFAULT_ACTIVATION_RANGE;
Frame = new RectangleF(location, image.Size);
Slider = slider;
RegisterPanGesture();
}
#endregion
#region Events, Overrides and Delegates
[Export("HandlePan")]
public void HandlePan(UIPanGestureRecognizer panGesture)
{
const double EndedAnimationDuration = 0.2d;
PointF newLocation;
float adjX;
if (panGesture != null)
{
newLocation = panGesture.LocationInView(this);
switch (panGesture.State)
{
case UIGestureRecognizerState.Began:
//User first taps the slider
if ((newLocation.X <= (Frame.X + Slider.Size.Width))
&& (newLocation.X >= 0))
{
InitialLocation = newLocation;
}
break;
case UIGestureRecognizerState.Changed:
//Moved their finger - make slider follow horizontal movements
adjX = Frame.X + (newLocation.X - InitialLocation.X);
if ((InitialLocation != PointF.Empty) && (adjX >= 0)
&& (adjX <= (Frame.Width - Slider.Size.Width)))
{
UIView.Animate(0d, delegate() {
_sliderView.Frame = new RectangleF(new PointF(adjX, 0),
_sliderView.Frame.Size);
});
//If the Slider comes within ActivationRange of end of this
//control, fire the Activate event
if ((Activate != null)
&& (adjX >=
(Frame.Width - Slider.Size.Width - ActivationRange)))
{
//Moved the slider all the way across the image view
Activate(this, EventArgs.Empty);
}
}
break;
case UIGestureRecognizerState.Cancelled:
case UIGestureRecognizerState.Failed:
case UIGestureRecognizerState.Ended:
//Lifted up finger - return slider to original position
InitialLocation = PointF.Empty;
UIView.Animate(EndedAnimationDuration, delegate() {
_sliderView.Frame = new RectangleF(new PointF(0, 0),
_sliderView.Frame.Size);
});
break;
}
}
}
//Delegate for allowing the pan gesture recognizer to receive touch.
public class ReceiveTouchGestureRecognizerDelegate
: UIGestureRecognizerDelegate
{
public override bool ShouldReceiveTouch (
UIGestureRecognizer recognizer,
UITouch touch)
{
return true;
}
}
#endregion
#region Helper Methods
protected void RegisterPanGesture()
{
UserInteractionEnabled = true;
UIPanGestureRecognizer pan = new UIPanGestureRecognizer();
pan.AddTarget(this, PanSelector);
pan.Delegate = new ReceiveTouchGestureRecognizerDelegate();
AddGestureRecognizer(pan);
}
#endregion
}
To use this control it's simply a matter of assigning images (Image and Slider properties) and adding the class as a sub-view:
UISlideToActivateImageView slideToActivate =
new UISlideToActivateImageView(new PointF(31, 214),
UIImage.FromFile("slidetoactivate.png"), UIImage.FromFile("slider.png"));
slideToActivate.Activate += delegate(object sender, EventArgs e) {
UIAlertView alert = new UIAlertView("Congratulations!",
"You've engaged the UISlideToActivateImageView!", null, "Okay");
alert.Show();
};
View.AddSubview(slideToActivate);