0

I have a xamarin forms image, and when the user taps on the image I want to get the x,y coordinates of where the user tapped. Not the x,y coordinates of the view per se, but the coordinates within the image. I have this working on Android below. What would the iOS custom renderer be like?

Create a interface for touch event:

public interface IFloorplanImageController
{
    void SendTouched();
}

Create a custom control for image:

public class FloorplanImage : Image, IFloorplanImageController
{
   public event EventHandler Touched;

 public void SendTouched()
 {
     Touched?.Invoke(this, EventArgs.Empty);
 }

 public Tuple<float, float> TouchedCoordinate
 {
     get { return (Tuple<float, float>)GetValue(TouchedCoordinateProperty); }
     set { SetValue(TouchedCoordinateProperty, value); }
 }

 public static readonly BindableProperty TouchedCoordinateProperty =
     BindableProperty.Create(
         propertyName: "TouchedCoordinate",
         returnType: typeof(Tuple<float, float>),
         declaringType: typeof(FloorplanImage),
         defaultValue: new Tuple<float, float>(0, 0),
         propertyChanged: OnPropertyChanged);

 public static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
   {
   }
}

Implement the custom renderer:

[assembly: ExportRenderer(typeof(FloorplanImage), typeof(FloorplanImageRenderer))]

namespace EmployeeApp.Droid.Platform
{
public class FloorplanImageRenderer : ImageRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
    {
        base.OnElementChanged(e);

        if (e.NewElement != null)
        {
            if (Control != null)
            {
                Control.Clickable = true;
                Control.SetOnTouchListener(ImageTouchListener.Instance.Value);
                Control.SetTag(Control.Id, new JavaObjectWrapper<FloorplanImage> { Obj = Element as FloorplanImage });
            }
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (Control != null)
            {
                Control.SetOnTouchListener(null);
            }
        }

        base.Dispose(disposing);
    }

    private class ImageTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
    {
        public static readonly Lazy<ImageTouchListener> Instance = new Lazy<ImageTouchListener>(
            () => new ImageTouchListener());

        public bool OnTouch(Android.Views.View v, MotionEvent e)
        {
            var obj = v.GetTag(v.Id) as JavaObjectWrapper<FloorplanImage>;
            var element = obj.Obj;
            var controller = element as IFloorplanImageController;
            if (e.Action == Android.Views.MotionEventActions.Down)
            {
                var x = e.GetX();
                var y = e.GetY();
                element.TouchedCoordinate = new Tuple<float, float>(x, y);
                controller?.SendTouched();
            }
            else if (e.Action == Android.Views.MotionEventActions.Up)
            {
            }
            return false;
        }
    }
}

public class JavaObjectWrapper<T> : Java.Lang.Object
{
    public T Obj { get; set; }
}
}

Use this control like this:

<local:FloorplanImage HeightRequest="300" x:Name="image" WidthRequest="300"
                  Aspect="AspectFit"  Touched="image_Touched" />

code behind:

private void image_Touched(object sender, EventArgs e)
{
  var cor = image.TouchedCoordinate;
}
stepheaw
  • 1,683
  • 3
  • 22
  • 35

1 Answers1

0

I found this Customer success example on github that does exactly this.

[assembly: ExportRenderer(typeof(CustomImage), typeof(CustomImageRenderer))]
namespace FormsImageTapGesture.iOS
{
    public class CustomImageRenderer : ImageRenderer
    {
        #region properties & fields
        // ---------------------------------------------------------------------------
        //
        // PROPERTIES & FIELDS
        //
        // ---------------------------------------------------------------------------
        private UIImageView nativeElement;
        private CustomImage formsElement;
        #endregion

        #region methods
        // ---------------------------------------------------------------------------
        //
        // METHODS
        //
        // ---------------------------------------------------------------------------

        //
        // Set up the custom renderer. In this case, that means set up the gesture
        // recognizer.
        //
        protected override void OnElementChanged(ElementChangedEventArgs<Image> e) {
            base.OnElementChanged (e);
            if (e.NewElement != null) {
                // Grab the Xamarin.Forms control (not native)
                formsElement = e.NewElement as CustomImage;
                // Grab the native representation of the Xamarin.Forms control
                nativeElement = Control as UIImageView;
                // Set up a tap gesture recognizer on the native control
                nativeElement.UserInteractionEnabled = true;
                UITapGestureRecognizer tgr = new UITapGestureRecognizer (TapHandler);
                nativeElement.AddGestureRecognizer (tgr);
            }
        }

        //
        // Respond to taps.
        //
        public void TapHandler(UITapGestureRecognizer tgr) {
            CGPoint touchPoint = tgr.LocationInView (nativeElement);
            formsElement.OnTapEvent ((int)touchPoint.X, (int)touchPoint.Y);
        }
        #endregion
    }
}
stepheaw
  • 1,683
  • 3
  • 22
  • 35