在 swiftUI 中捕获 onLongPressGesture 的 touchDown 位置?

Ada*_* VZ 8 ios swift swiftui

我正在尝试实现一个自定义上下文菜单,该菜单将在用户触摸的位置长按后出现。我一直无法找到一种方法来捕获 onLongPressGesture 的触地事件的 XY 位置。

这就是我开始的地方

struct ExampleView: View {
    @State var showCustomContextMenu = false
    @State var longPressLocation = CGPoint.zero
    
    var body: some View {
        Rectangle()
            .foregroundColor(Color.green)
            .frame(width: 100.0, height: 100.0)
            .onLongPressGesture {
                print("OnLongPressGesture")
                self.showCustomContextMenu = true
            }
            .overlay(
                Rectangle()
                    .foregroundColor(Color.red)
                    .frame(width: 50.0, height: 50.0)
                    .position(longPressLocation) // <----- this is what I need to capture.
                    .opacity( (showCustomContextMenu) ? 1 : 0 )
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

看了这个问题(以及答案中链接的其他问题)后,我尝试了以下操作。

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

struct ExampleView: View {
    @State var showCustomContextMenu = false
    @State var longPressLocation = CGPoint.zero
    
    var body: some View {
        ZStack{
            Rectangle()
                .foregroundColor(Color.green)
                .frame(width: 100.0, height: 100.0)
                .onLongPressGesture {
                    print("OnLongPressGesture")
                    self.showCustomContextMenu = true
                }
                .overlay(
                    Rectangle()
                        .foregroundColor(Color.red)
                        .frame(width: 50.0, height: 50.0)
                        .position(longPressLocation)
                        .opacity( (showCustomContextMenu) ? 1 : 0 )
            )
            TapView { point in
                self.longPressLocation = point
                print("Point: \(point)")
            }.background(Color.gray).opacity(0.5)
        }
    }
}

struct TapView: UIViewRepresentable {
    var tappedCallback: ((CGPoint) -> 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: ((CGPoint) -> Void)

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

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

    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)

这几乎可以工作,但是,这给我留下的问题是,由于 Rectangle() 和 TapView() 位于 ZStack 中,具体取决于我将它们放在代码中的位置,我要么得到touchDown位置,要么得到onLongPressGesture,但不能同时得到两者。

我看过但遇到类似问题的其他问题链接如下

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

Swift:长按手势识别器 - 检测点击和长按 这可能是我正在寻找的,但我不确定如何使其适应 SwiftUI。

Asp*_*eri 10

这是可能方法的演示。它需要两种手势的组合:长按检测长按和拖动检测位置。

使用 Xcode 12 / iOS 14 进行测试。(在以下系统上可能需要添加self.某些属性使用)

演示

struct ExampleView: View {
    @State var showCustomContextMenu = false
    @State var longPressLocation = CGPoint.zero

    var body: some View {
        Rectangle()
            .foregroundColor(Color.green)
            .frame(width: 100.0, height: 100.0)
            .onTapGesture { showCustomContextMenu = false } // just for demo
            .gesture(LongPressGesture(minimumDuration: 1).sequenced(before: DragGesture(minimumDistance: 0, coordinateSpace: .local))
                .onEnded { value in
                    switch value {
                        case .second(true, let drag):
                            longPressLocation = drag?.location ?? .zero   // capture location !!
                            showCustomContextMenu = true
                        default:
                            break
                    }
            })
            .overlay(
                Rectangle()
                    .foregroundColor(Color.red)
                    .frame(width: 50.0, height: 50.0)
                    .position(longPressLocation)
                    .opacity( (showCustomContextMenu) ? 1 : 0 )
                    .allowsHitTesting(false)
        )
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是行不通的——在用户移动手指之前,“拖动”为零!如果她举起它但没有移动,则无法读取位置。我真的很不喜欢 SwiftUI 从手势事件中遗漏有用的信息。 (4认同)