在swiftUI中捕获onLongPressGesture的touchDown位置?
Posted
技术标签:
【中文标题】在swiftUI中捕获onLongPressGesture的touchDown位置?【英文标题】:Capture touchDown location of onLongPressGesture in swiftUI? 【发布时间】:2020-07-10 15:40:05 【问题描述】:我正在尝试实现一个自定义上下文菜单,该菜单将在长按用户触摸的位置后出现。我一直无法找到一种方法来捕获 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 )
)
在查看了这个问题(以及答案中链接的其他 SO 问题)后,我尝试了以下操作。
How do you detect a SwiftUI touchDown event with no movement or duration?
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
这几乎可行,但是,这给我留下的问题是,由于 Rectangle() 和 TapView() 位于 ZStack 中,这取决于我将它们放置在代码中的位置 either touchDown 位置或 onLongPressGesture,但不能同时使用。
我查看过但遇到类似问题的其他 SO 问题链接如下
How to detect a tap gesture location in SwiftUI?
Swift: Long Press Gesture Recognizer - Detect taps and Long Press 这可能是我正在寻找的,但我不确定如何使其适应 SwiftUI。
【问题讨论】:
【参考方案1】:这是一个可能的方法的演示。它需要两种手势的组合:长按检测长按和拖动检测位置。
使用 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)
)
【讨论】:
由于某种原因,它只适用于画布预览。在模拟器中,当我长按时,位置是.zero
,所以 Rectangle() 显示在 .zero
CGPoint
以上是关于在swiftUI中捕获onLongPressGesture的touchDown位置?的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI捕获键盘提交动作在iOS15之前和之后的兼容实现