您如何检测没有移动或持续时间的 SwiftUI touchDown 事件?

eli*_*ade 16 gesture swift swiftui

我试图检测手指何时第一次接触 SwiftUI 中的视图。我可以用 UIKit Events 很容易地做到这一点,但在 SwiftUI 中无法解决这个问题。

我已经尝试了最小移动为 0 的 DragGesture,但在您的手指移动之前它仍然不会改变。

TapGesture 仅在您抬起手指时才起作用,并且无论我将参数设置为什么,LongPressGesture 都不会足够快地触发。

DragGesture(minimumDistance: 0, coordinateSpace: .local).onChanged({ _ in print("down")})

LongPressGesture(minimumDuration: 0.01, maximumDistance: 100).onEnded({_ in print("down")})
Run Code Online (Sandbox Code Playgroud)

我想在手指与视图接触后立即检测 touchDown 事件。Apple 的默认手势对距离或时间都有限制。

更新:这不再是问题,因为 Apple 似乎更新了 DragGesture 的工作方式,或者我可能遇到了特定的上下文错误。

nil*_*ils 23

您可以.updating像这样使用修饰符:

struct TapTestView: View {

    @GestureState private var isTapped = false

    var body: some View {

        let tap = DragGesture(minimumDistance: 0)
            .updating($isTapped) { (_, isTapped, _) in
                isTapped = true
            }

        return Text("Tap me!")
            .foregroundColor(isTapped ? .red: .black)
            .gesture(tap)
    }
}
Run Code Online (Sandbox Code Playgroud)

一些注意事项:

  • 零最小距离确保立即识别手势
  • @GestureState属性包装自动将其值重置为初始值时的姿态结束。这样您只需担心设置isTappedtrue. false当交互结束时,它会自动再次出现。
  • updating修改有三个参数这种怪异的关闭。在这种情况下,我们只对中间的感兴趣。它是inout的包装值的参数GestureState,因此我们可以在此处设置它。第一个参数是手势的当前值;第三个是Transaction包含一些动画上下文。

  • @eli_slade 这个答案应该被接受为最佳答案恕我直言 (2认同)

sup*_*cio 13

您可以通过以下方式创建视图修改器:

extension View {
    func onTouchDownGesture(callback: @escaping () -> Void) -> some View {
        modifier(OnTouchDownGestureModifier(callback: callback))
    }
}

private struct OnTouchDownGestureModifier: ViewModifier {
    @State private var tapped = false
    let callback: () -> Void

    func body(content: Content) -> some View {
        content
            .simultaneousGesture(DragGesture(minimumDistance: 0)
                .onChanged { _ in
                    if !self.tapped {
                        self.tapped = true
                        self.callback()
                    }
                }
                .onEnded { _ in
                    self.tapped = false
                })
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以像这样使用它:

struct MyView: View {
    var body: some View {
        Text("Hello World")
            .onTouchDownGesture {
                print("View did tap!")
            }
    }
}
Run Code Online (Sandbox Code Playgroud)


Joe*_*Joe 8

如果将这两个问题的代码结合起来:

如何在 SwiftUI 中检测点击手势位置?

UITapGestureRecognizer - 让它在触地时工作,而不是触地?

你可以做这样的事情:

ZStack {
    Text("Test")
    TapView {
        print("Tapped")
    }
}
Run Code Online (Sandbox Code Playgroud)
struct TapView: UIViewRepresentable {
    var tappedCallback: (() -> Void)

    func makeUIView(context: UIViewRepresentableContext<TapView>) -> TapView.UIViewType {
        let v = UIView(frame: .zero)
        let gesture = SingleTouchDownGestureRecognizer(target: context.coordinator,
                                                       action: #selector(Coordinator.tapped))
        v.addGestureRecognizer(gesture)
        return v
    }

    class Coordinator: NSObject {
        var tappedCallback: (() -> Void)

        init(tappedCallback: @escaping (() -> Void)) {
            self.tappedCallback = tappedCallback
        }

        @objc func tapped(gesture:UITapGestureRecognizer) {
            self.tappedCallback()
        }
    }

    func makeCoordinator() -> TapView.Coordinator {
        return Coordinator(tappedCallback:self.tappedCallback)
    }

    func updateUIView(_ uiView: UIView,
                      context: UIViewRepresentableContext<TapView>) {
    }
}

class SingleTouchDownGestureRecognizer: UIGestureRecognizer {
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
        if self.state == .possible {
            self.state = .recognized
        }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
        self.state = .failed
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
        self.state = .failed
    }
}
Run Code Online (Sandbox Code Playgroud)

我们肯定可以做一些抽象,以便使用更像其他 SwiftUI 手势,但这是一个开始。希望苹果在某个时候支持这一点。


eha*_*ro2 5

这是一个检测状态之间变化以及触摸坐标(在Text View本例中)的解决方案:

我添加了一个枚举来管理状态(对于那些UIKit怀旧使用开始、移动和结束)

导入 SwiftUI

struct ContentView: View {
    @State var touchPoint = CGPoint(x: 0, y: 0)
    @State var touchState = TouchState.none
    var body: some View {
        Text("\(touchState.name): \(Int(self.touchPoint.x)), \(Int(self.touchPoint.y))")
            .border(Color.red).font(.largeTitle)
            .gesture(
                DragGesture(minimumDistance: 0)
                    .onChanged({ (touch) in
                        self.touchState = (self.touchState == .none || self.touchState == .ended) ? .began : .moved
                        self.touchPoint = touch.location
                    })
                    .onEnded({ (touch) in
                        self.touchPoint = touch.location
                        self.touchState = .ended
                    })
            )
    }
}
enum TouchState {
    case none, began, moved, ended
    var name: String {
        return "\(self)"
    }
}
Run Code Online (Sandbox Code Playgroud)


Ant*_*ton 5

实际上,@eli_slade,您只需要这样:

LongPressGesture().onChanged {_ in print("down") }
Run Code Online (Sandbox Code Playgroud)

这是一个演示应用程序:

应用演示

这是它的代码:

struct ContentView: View {
    @State var isRed = false

    var body: some View {
        Circle()
            .fill(isRed ? Color.red : Color.black)
            .frame(width: 150, height: 150)
            .gesture(LongPressGesture().onChanged { _ in self.isRed.toggle()})
    }
}
Run Code Online (Sandbox Code Playgroud)


Pet*_*isu 5

上述方法都不能完美地工作,就像DragGesture使表格无法滚动一样

经过长时间的寻找,我找到了这个圣杯!

注意:它是公开可用的,但 _ 前缀意味着它可能不适合生产

但效果仍然很好

._onButtonGesture(pressing: { pressing in
                    
}, perform: {

})
Run Code Online (Sandbox Code Playgroud)