(For SwiftUI, not vanilla UIKit) Very simple example code to, say, display red boxes on a gray background:
struct ContentView : View { @State var points:[CGPoint] = [CGPoint(x:0,y:0), CGPoint(x:50,y:50)] var body: some View { return ZStack { Color.gray .tapAction { // TODO: add an entry to self.points of the location of the tap } ForEach(self.points.identified(by: \.debugDescription)) { point in Color.red .frame(width:50, height:50, alignment: .center) .offset(CGSize(width: point.x, height: point.y)) } } } }
I'm assuming instead of tapAction, I need to have a TapGesture or something? But even there I don't see any way to get information on the location of the tap. How would I go about this?
Starting from iOS 16 and macOS 13, we can get the location of a tap in SwiftUI. The new onTapGesture(count:coordinateSpace:perform:) overload of onTapGesture() modifier provides the location in its perform closure and allows us to request the location in local or global coordinate space.
The Tap gesture detects a deliberate tap on a component, in a manner that is more restrictive than onPress . Tap imposes the following additional restrictions with respect to the initial down and final pointer up events: They must occur within a short distance, to rule out drag-like gestures.
UITapGestureRecognizer is a concrete subclass of UIGestureRecognizer . For gesture recognition, the specified number of fingers must tap the view a specified number of times. Although taps are discrete gestures, they're discrete for each state of the gesture recognizer.
Adding a Tap Gesture Recognizer to an Image View in Interface Builder. Open Main. storyboard and drag a tap gesture recognizer from the Object Library and drop it onto the image view we added earlier. The tap gesture recognizer appears in the Document Outline on the left.
Well, after some tinkering around and thanks to this answer to a different question of mine, I've figured out a way to do it using a UIViewRepresentable (but by all means, let me know if there's an easier way!) This code works for me!
struct ContentView : View { @State var points:[CGPoint] = [CGPoint(x:0,y:0), CGPoint(x:50,y:50)] var body: some View { return ZStack(alignment: .topLeading) { Background { // tappedCallback location in self.points.append(location) } .background(Color.white) ForEach(self.points.identified(by: \.debugDescription)) { point in Color.red .frame(width:50, height:50, alignment: .center) .offset(CGSize(width: point.x, height: point.y)) } } } } struct Background:UIViewRepresentable { var tappedCallback: ((CGPoint) -> Void) func makeUIView(context: UIViewRepresentableContext<Background>) -> UIView { let v = UIView(frame: .zero) let gesture = UITapGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.tapped)) v.addGestureRecognizer(gesture) return v } class Coordinator: NSObject { var tappedCallback: ((CGPoint) -> Void) init(tappedCallback: @escaping ((CGPoint) -> Void)) { self.tappedCallback = tappedCallback } @objc func tapped(gesture:UITapGestureRecognizer) { let point = gesture.location(in: gesture.view) self.tappedCallback(point) } } func makeCoordinator() -> Background.Coordinator { return Coordinator(tappedCallback:self.tappedCallback) } func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<Background>) { } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With