SwiftUI:长按手势保持仅 1 秒

SwiftUI: Longpress Gesture Hold for only 1 Second

在SwiftUI上使用长按手势只需保持长按手势1秒,然后自动松开长按。我希望用户最多按下 1 分钟或更长时间。这可能吗,如何实现。

查看我下面的代码,它目前只支持 1 秒持续时间的长按手势。

    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()
    }
}

无论用户是否抬起手指,LongPressGesture 都会在最短时间后更新。在这里查看如何注册到 onEnded,我猜这是你想要等待的。即当用户 his/hers 手指离开屏幕时 - https://developer.apple.com/documentation/swiftui/longpressgesture

这里有一个非常基本的方法,您可以基于以下代码进行构建:

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()
    }
}