Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI - Increment number on long press gesture, then stop once gesture is released

I'm attempting to create a stepper where a number increments rapidly on a longpress gesture and stops when the user releases.

So far, I get the increment to work on the longPress, but when I release the timer still goes, continuing to increment the state.

What can I do to resolve this issue that when the user releases the press, the timer stops.

struct CustomFoodItemView: View {
    @State var foodName = ""
    @State var proteinAmount = 1
    @State var carbAmount = 1
    @State var fatAmount = 1
    
    @State private var timer: Timer?
    @State var isLongPressing = false
    
    var body: some View {
        VStack{
            
            VStack{
                Text("Food Name")
                TextField("", text: $foodName)
                    .multilineTextAlignment(.center)
                    .border(.white)
                    .padding(.trailing, 10)
                    .frame(width:100, height:10)
            }
            HStack{
                Text(String(proteinAmount) + "g")
                    .frame(width:50, height:50)
                
                Image(systemName: "plus.circle.fill")
                    .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 30, height: 30)
                        .foregroundColor(Color("SuccessButtonColor"))
                        .simultaneousGesture(LongPressGesture(minimumDuration: 0.2).onChanged { _ in
                                      print("long press")
                                      self.isLongPressing = true
                            if self.isLongPressing == true{
                                self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { _ in
                                    proteinAmount += 1
                                })
                            } 
                                  }
                                .onEnded { _ in
                            print("stopped") //why won't you stop
                                    self.isLongPressing = false
                                })
                       }
              }

enter image description here

like image 935
Swink Avatar asked Dec 05 '25 17:12

Swink


1 Answers

Two things:

  1. You don't stop the timer at all, to stop it, you have to invalidate it (as Leo mentioned). You can do that with self.timer?.invalidate().
  2. The .onEndedof the LongPressGesture will be called when the LongPressGesture has been recognized by pressing the button longer than the minimumDuration time, it does not handle the event when the button will be released. So you don't want to stop the timer on .onEnded.

Combined LongPressGesture and DragGesture

I tried your code and an approach could be to use the LongPressGesture to start the timer and a DragGesture with a minimum distance of 0 to recognize the button release.

This would look like:

struct CustomFoodItemView: View {
    @State var foodName = ""
    @State var proteinAmount = 0
    @State var carbAmount = 0
    @State var fatAmount = 0
    let maxValue = 50
    let minValue = 0
    
    @State private var timer: Timer?
    
    var body: some View {
        
        // a drag gesture that recognizes the release of the button to stop the timer, set minimumDistance to 0 to ensure no dragging is required
        let releaseGesture = DragGesture(minimumDistance: 0)
            .onEnded { _ in
                self.timer?.invalidate()
                print("Timer stopped")
            }
        
        // a long press gesture to activate timer and start increasing the proteinAmount
        let longPressGestureIncrease = LongPressGesture(minimumDuration: 0.2)
            .onEnded { value in
                self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { _ in
                    if proteinAmount < maxValue {
                        proteinAmount += 1
                    }
                })
                print("Timer started")
            }


        // a long press gesture to activate timer and start decreasing the proteinAmount
        let longPressGestureDecrease = LongPressGesture(minimumDuration: 0.2)
            .onEnded { value in
                self.timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true, block: { _ in
                    if proteinAmount > minValue {
                        proteinAmount -= 1
                    }
                })
                print("Timer started")
            }
    
        // a combined gesture that forces the user to long press before releasing for increasing the value
        let combinedIncrease = longPressGestureIncrease.sequenced(before: releaseGesture)
    
        // a combined gesture that forces the user to long press before releasing for decreasing the value
        let combinedDecrease = longPressGestureDecrease.sequenced(before: releaseGesture)
        
        VStack{
            
            VStack{
                Text("Food Name")
                TextField("", text: $foodName)
                    .multilineTextAlignment(.center)
                    .border(.white)
                    .padding(.trailing, 10)
                    .frame(width:100, height:10)
            }
            HStack{
                Image(systemName: "minus.circle.fill")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 30, height: 30)
                    .foregroundColor(.red)
                    .gesture(combinedDecrease)

                Text(String(proteinAmount) + "g")
                    .frame(width:50, height:50)
                
                Image(systemName: "plus.circle.fill")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 30, height: 30)
                    .foregroundColor(.red)
                    .gesture(combinedIncrease)
            }
        }
    }
}

Please see the gif with the the few items from the code snippet (the number increases after starting the long press and stops when releasing the "+" button):

Increasing number by activated timer

Indrease and Decrease number by activating timers

like image 102
Sebastian Fox Avatar answered Dec 09 '25 03:12

Sebastian Fox



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!