skip to Main Content

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


  1. 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

    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
  3. 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

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search