skip to Main Content

Using the Long press gestures on SwiftUI only keep the long press hold gesture for 1 second then automatically releases the long press. I would like for the user to press up to 1 minute or more. Is this possible and how can it be done.

Check out my code below, which currently only supports a 1-second duration long-press gesture.

    struct IgnitionDriveView: View {
    
    @GestureState private var drivingGestureState = false
    @GestureState private var reverseGestureState = false
    @State private var showDriveAlert = true
    @State private var showOutOfGasAlert = false
    @State var distanceCovered: Float = 1.0
    
    var body: some View {
        
        let circleShape = Circle()
        let driveGesture = LongPressGesture(minimumDuration: 1)
            .updating($drivingGestureState) { (currentState, gestureState, transaction) in
                gestureState = currentState
            }.onChanged { _ in
                if distanceCovered < 1000 {
                    self.distanceCovered += 10
                } else {
                    showOutOfGasAlert = true
                }
            }
        
        let reverseGesture = LongPressGesture(minimumDuration: 1)
            .updating($reverseGestureState) { (currentState, gestureState, transaction) in
                gestureState = currentState
            }.onChanged { _ in
                if distanceCovered > 0 {
                    self.distanceCovered -= 10
                }
            }
        
        VStack(alignment: .leading) {
            Text("Distance Covered in Km: (distanceCovered)")
                .font(.headline)
            ProgressView(value: distanceCovered > 0 ? distanceCovered : 0, total: 1000)
                .frame(height: 40)
            
            HStack {
                ZStack {
                    circleShape.strokeBorder(style: StrokeStyle(lineWidth: 2))

                    circleShape
                        .fill(drivingGestureState ? .white : .red)
                        .frame(width: 100, height: 100, alignment: .center)
                    
                    Text("D")
                        .bold()
                        .padding()
                        .foregroundColor(.green)
                        .font(.title)
                }.foregroundColor(.green)
                    .gesture(driveGesture)
                
                Spacer()
                
                ZStack {
                    circleShape.strokeBorder(style: StrokeStyle(lineWidth: 2))

                    circleShape
                        .fill(reverseGestureState ? .white : .red)
                        .frame(width: 100, height: 100, alignment: .center)
                    
                    Text("R")
                        .bold()
                        .padding()
                        .foregroundColor(.red)
                        .font(.title)
                }.foregroundColor(.green)
                    .gesture(reverseGesture)
                    
            }.padding()
        }.alert("Press D to Drive and R to Reverse", isPresented: $showDriveAlert) {
            Button("Okay") { showDriveAlert = false }
        }.alert("You ran out of Gas, Reverse to Gas Station", isPresented: $showOutOfGasAlert) {
            Button("Sucks, but fine!") { showOutOfGasAlert = false }
        }
        .padding()
    }
}

Screenshot

2

Answers


  1. The LongPressGesture is updating after the minimum time no matter if the user lifts its finger or not. Take a look here on how to register to the onEnded even which I guess is what you want to wait for. i.e when the user takes his/hers finger off screen – https://developer.apple.com/documentation/swiftui/longpressgesture

    Login or Signup to reply.
  2. here is a very basic approach that you can build on, based on the code in:

    https://adampaxton.com/make-a-press-and-hold-fast-forward-button-in-swiftui/

    struct IgnitionDriveView: View {
        
        @State private var timer: Timer?
        @State var isLongPressD = false
        @State var isLongPressR = false
        
        @State private var showDriveAlert = true
        @State private var showOutOfGasAlert = false
        @State var distanceCovered: Float = 0.0
    
        private func circleShape(isPressed: Binding<Bool>) -> some View  {
            Button(action: {
                if isPressed.wrappedValue {
                    isPressed.wrappedValue.toggle()
                    timer?.invalidate()
                }
            }) {
                ZStack {
                    Circle().strokeBorder(style: StrokeStyle(lineWidth: 2))
                    Circle().fill(isPressed.wrappedValue ? .white : .red)
                }.frame(width: 100, height: 100, alignment: .center)
            }
        }
        
        var body: some View {
            
            VStack(alignment: .leading) {
                Text("Distance Covered in Km: (distanceCovered)").font(.headline)
                ProgressView(value: distanceCovered > 0 ? distanceCovered : 0, total: 1000).frame(height: 40)
                
                HStack {
                    ZStack {
                        circleShape(isPressed: $isLongPressD)
                        .simultaneousGesture(LongPressGesture(minimumDuration: 0.2).onEnded { _ in
                            isLongPressD = true
                            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
                                if distanceCovered < 1000 {
                                    distanceCovered += 10
                                } else {
                                    showOutOfGasAlert = true
                                }
                            })
                        })
                        
                        Text("D").bold().padding().foregroundColor(.green).font(.title)
                    }.foregroundColor(.green)
                    
                    Spacer()
                    
                    ZStack {
                        circleShape(isPressed: $isLongPressR)
                        .simultaneousGesture(LongPressGesture(minimumDuration: 0.2).onEnded { _ in
                            isLongPressR = true
                            timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
                                if distanceCovered > 0 {
                                    distanceCovered -= 10
                                }
                            })
                        })
                        
                        Text("R").bold().padding().foregroundColor(.blue).font(.title)
                    }.foregroundColor(.green)
                    
                }.padding()
            }.alert("Press D to Drive and R to Reverse", isPresented: $showDriveAlert) {
                Button("Okay") { showDriveAlert = false }
            }.alert("You ran out of Gas, Reverse to Gas Station", isPresented: $showOutOfGasAlert) {
                Button("Sucks, but fine!") { showOutOfGasAlert = false }
            }
            .padding()
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search