How to get local tap position in onTapGesture?

Viewed 5107

Have some rendered image and need to determine tap position relative to image. Seems to be simple, but can't find the answer

  var body: some View {
    VStack {
      Image(uiImage: drawRectangle()) // some rendered image
        .onTapGesture {
          // print(x, y)?
        }
2 Answers

Found this solution:

        .gesture(
            DragGesture(minimumDistance: 0, coordinateSpace: .global)
                .onChanged { value in
                  self.position = value.location
                }
                .onEnded { _ in
                  self.position = .zero
                }
        )

setting minimumDistance: 0 will call onChanged immediately

I did try using DragGesture with minimumDistance: 0 but it hijacks my table view's scroll gesture.

So I preferred to use a proper UITapGestureRecognizer added to a plain transparent UIView embed in aTappableView SwitUI UIViewRepresentable instead.

struct TappableView: UIViewRepresentable
{
    var tapCallback: (UITapGestureRecognizer) -> Void

    typealias UIViewType = UIView

    func makeCoordinator() -> TappableView.Coordinator
    {
        Coordinator(tapCallback: self.tapCallback)
    }

    func makeUIView(context: UIViewRepresentableContext<TappableView>) -> UIView
    {
        let view = UIView()
        view.addGestureRecognizer(UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.handleTap(sender:))))
        return view
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<TappableView>)
    {
    }

    class Coordinator
    {
        var tapCallback: (UITapGestureRecognizer) -> Void

        init(tapCallback: @escaping (UITapGestureRecognizer) -> Void)
        {
            self.tapCallback = tapCallback
        }

        @objc func handleTap(sender: UITapGestureRecognizer)
        {
            self.tapCallback(sender)
        }
    }
}

Then just add it as a .overlay to the SwiftUI view you want taps added to.

.overlay(
    TappableView { gesture in
        // Just use the regular UIKit gesture as you want!
})
Related