swiftui

How to fix DragGesture() does not move smoothly when I use onTapGesture together


I'm currently developing an application using SwiftUI and trying to make a swipe component.

When I use DragGesture and onTapGesture together, that swipe motion does not move smoothly...

If I make onTapGesture disable the swipe motion work well.

But I want to use both of them together.

How could I solve this problem?


Here is the code:

import SwiftUI

struct SwipeTest1: View {
    
    @State var offset : CGFloat = 0
    @State var isOn = false
    @State var isOnTap = false
    
    var body: some View {
            VStack{
                Text(isOnTap ? "isOnTap: ON" : "isOnTap: OFF")
                ZStack{
                    Capsule()
                        .fill(Color.primary.opacity(0.1))
                    VStack{
                        Spacer()
                        Circle()
                            .fill(isOn ? Color.blue : Color.green )
                            .frame(width: 60, height: 60)
                            .offset(y:offset)
                            .onTapGesture {isOnTap.toggle()} // ← If I comment out this line,the swipe motion work well.  
                            .gesture(DragGesture().onChanged(isOn ? onChangedOn(value:) : onChangedOff(value:))
                                        .onEnded(isOn ? onEndOn(value:) : onEndOff(value:))
                            )
                    }
                }
                .frame(width: 60, height: 120)
            }
    }
    
    
    func onChangedOff(value: DragGesture.Value){
        if value.translation.height < 0 && offset >=  -60{
            offset = value.translation.height
        }
    }
    
    func onChangedOn(value: DragGesture.Value){
        if  offset <=  0{
            offset =  value.translation.height - 60
        }
    }
    
    func onEndOff(value: DragGesture.Value){
        if offset < -40 {
            offset = -60
            isOn = true
        }else{
            offset = 0
        }
    }
    
    func onEndOn(value: DragGesture.Value){
        if offset > -20 {
            offset = 0
            isOn = false
        }else{
            offset = -60
        }
    }
}

Xcode: Version 12.3

iOS: 14.0


Solution

  • You can use simultaneousGesture:

    Circle()
        .fill(isOn ? Color.blue : Color.green )
        .frame(width: 60, height: 60)
        .offset(y:offset)
        .onTapGesture {isOnTap.toggle()}
        .simultaneousGesture(DragGesture().onChanged(isOn ? onChangedOn(value:) : onChangedOff(value:))
                    .onEnded(isOn ? onEndOn(value:) : onEndOff(value:))
        )
    

    Also, you can adjust the minimumDistance of the DragGesture (by default it's 10):

    DragGesture(minimumDistance: 10)