【问题标题】:SwiftUI: Longpress Gesture Hold for only 1 SecondSwiftUI:长按手势保持 1 秒
【发布时间】:2022-01-03 07:30:59
【问题描述】:

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

【问题讨论】:

  • 1分60秒。
  • 增加最小值会延长长按,但在用户按下视图时更新不会传递到updated()。
  • 这是否回答了您的问题stackoverflow.com/a/61524230/12299030
  • 您能否准确解释您想要实现的目标,而不是说“我希望用户按下最多 1 分钟或更长时间。”。你到底想“更新”什么,什么时候结束?
  • 我希望计数器 [distanceCovered] 只要用户按下视图并更新进度视图值直到计数器达到 1000,就可以更新 [增加/减少]。希望这很清楚.

标签: ios swift swiftui gesture


【解决方案1】:

这是一个非常基本的方法,您可以根据以下代码进行构建:

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

【讨论】:

  • 感谢@workingdog,我认为最终我缺少的是使用计时器和同步手势。让我试一试,查看文档以了解代码的不同之处。但我认为这是最终的答案。
【解决方案2】:

无论用户是否抬起手指,LongPressGesture 都会在最短时间后更新。在此处查看如何注册到 onEnded,即使我猜这是您想要等待的。即当用户将他/她的手指从屏幕上移开时 - https://developer.apple.com/documentation/swiftui/longpressgesture

【讨论】:

  • 我希望 Longpress 在用户释放长按之前继续更新。 onEnded 似乎只是延迟更新,直到用户释放长按。
  • 我明白了。这不是 LongPressGesture 的用例。您必须使用它来触发开始和结束事件的检测。并在检测到触发器后使用计时器或其他方法
  • 感谢@CloudBalancing,我认为使用计时器最终将帮助我实现对它应该如何工作的期望。你能在上面检查工作狗的答案吗?我认为它采用了使用计时器的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-21
  • 1970-01-01
  • 2016-04-05
  • 1970-01-01
  • 1970-01-01
  • 2019-01-16
  • 2018-01-18
相关资源
最近更新 更多