Search code examples
iosbuttonswiftuigesture

SwiftUI handling button/view with onTapGesture and onLongPressGesture with a release action


I have a view that has both onTapGesture and onLongPressGesture simultaneously. The issue is that the implementation of my onLongPressGesture prevents onTapGesture from ever being called.

Here is some code

View()
      .onTapGesture {
           action_1
       }
      .onLongPressGesture(minimumDuration: 0.5, maximumDistance: 10, pressing: {
                                    pressing in
                                    self.isPressing = pressing
                                    if (pressing) {action_2}
                                    if !pressing {action_3}
                                }, perform: {})

The pressing argument in .onLongPressGesture detects if a user is pressing the view/button, and will always perform the .onLongPressGesture, regardless of what the minimumDuration is.


Solution

  • Edit

    Snapchat shutter where you can tap to take a picture, hold the button to start recording a video, then release the button to stop recording the video. That is why there are three actions that need to be performed.

    This is tricky. Here's what I did:

    1. onTapGesture, for the tap gesture
    2. LongPressGesture, for a 0.5 second delay. Once the 0.5 seconds is over (.onEnded), start the recording.
    3. DragGesture, for observing when the finger lift off the screen. When this happens, stop the recording.
    struct ContentView: View {
        
        /// for visual indicators
        @State var recording = false
        @State var tapped = false
        
        var body: some View {
            let longPressDrag = LongPressGesture(minimumDuration: 0.5) /// 2.
                .onEnded { _ in /// 0.5 seconds is over, start recording
                    print("Long press start")
                    recording = true
                }
                .sequenced(before: DragGesture(minimumDistance: 0)) /// 3.
                .onEnded { _ in /// finger lifted, stop recording
                    print("Long press release")
                    recording = false
                }
            
            Circle()
                .fill(recording ? Color.red : Color.blue)
                .opacity(tapped ? 0.5 : 1)
                .frame(width: 100, height: 100)
                
                .onTapGesture { /// 1.
                    print("Tapped")
                    
                    tapped = true
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { tapped = false }
                }
                .gesture(longPressDrag)
    
        }
    }
    

    Result:

    Snapchat-style button

    Old answer: The pressing parameter isn't for performing actions like action_2. You can, but it's more commonly used for changing @State, so for example highlighting the view in green when it's pressed. You can find more information in the community documentation.

    Instead, what you probably want is to call your action (action_2/print("long")) in the perform closure.

    struct ContentView: View {
        var body: some View {
            Text("Hi")
                .font(.title)
                .onTapGesture {
                    print("tap")
                }
                .onLongPressGesture(minimumDuration: 0.5, maximumDistance: 10) {
                    print("long")
                }
        }
    }
    

    Result:

    Both tap and long press work