onTapGesture action on a Text moving with animation

Viewed 296

I am trying to implement an action on tap gesture on a Text which is moving with animation using SwiftUI.

When the Text is moving, the tap gesture seems to register only when I tap at the final destination of the Text, and not when I tap on the moving Text

struct ContentView: View {
    @State var offsetY : CGFloat = 0
    var body: some View {
        VStack {
            Text("Animate")
                .onTapGesture {
                    withAnimation(.linear(duration: 15)){
                        offsetY = 200
                    }
                }
            Text("Hello, world!")
                .offset(y: offsetY)
                .onTapGesture {
                    print("Tap")
                }
        }
    }
}

Is there a way to make tapGesture register when I tap on the moving Text ?

By the way, it works when I encapsulate the Text in a NavigationLink, but I don't want to have a NavigationLink in my case.

struct ContentView: View {
    @State var offsetY : CGFloat = 0
    var body: some View {
        VStack {
            Text("Animate")
                .onTapGesture {
                    withAnimation(.linear(duration: 15)){
                        offsetY = 100
                    }
                }
            NavigationLink(destination: EmptyView()) {
                Text("Hello, world!")
            }.offset(y: offsetY)
                .onTapGesture {
                    print("Tap")
                }
        }
    }
}
3 Answers

you can work around that by using a button with the same position of the text you want to tap on it while animating

struct ContentView: View {
@State var offsetY : CGFloat = 0
@State var counter = 0 // just for counting taps 
var body: some View {
    VStack {
        Text("Animate")
            .onTapGesture {
                withAnimation(.linear(duration: 15)){
                    offsetY = 200
                }
            }


        Text("Hello, world!").offset(y: offsetY) // the text that animates
        
        Button {
            counter += 1
            print("tap \(counter)")
        } label: {
          // remove text here so that button will be hidden 
            Text("").position(y: offsetY)
        }
         // the button with the same position to the animating offset of text   
    }.frame(width: 500, height: 500, alignment: .center)
}
}

enter image description here

try replacing your

 .onTapGesture {
     print("Tap")
 }

with

.simultaneousGesture(
  TapGesture()
      .onEnded { _ in
          print("Tap")
      }
 )

Works well for me on macos 12.5, using Xcode 13.3, targets ios-15 macCatalyst 12.3, tested on real devices only.

By default animatable value is applied instantly and we observe just drawing animation to that final value. In this case frame is jumping to the end and tap works there. To interfere in this we can use custom animatable modifier, which just process view progressively and frame is always updated during animation so tappable.

Tested with Xcode 13.4 / iOS 15.5

enter image description here

Main part:

Text("Hello, world!")
    .onTapGesture {
        print("Tap")
    }
    .modifier(MovableViewModifier(value: offsetY))   // << here !!

Complete test module is here

Related