SwiftUI:同时拖动父视图和子视图,但用单独的手指

Chr*_*itt 9 ipad ios swift swiftui ipados

我有一个应用程序,它有一个父视图(graphView,带有粗蓝色边框)和一个子视图(nodeView,绿色框)。每个都有自己的 SwiftUI DragGesture 处理程序。我希望能够用不同的手指移动绿色节点和蓝色边框图。

\n

目标:当我主动拖动 \xe2\x80\x9c 蓝色边框图时,将 \xe2\x80\x9cgreen 节点 \xe2\x80\x9d 保持在我的手指下。\xe2\x80\x9d

\n

问题:绿色节点 \xe2\x80\x9cghosts\xe2\x80\x9d,即似乎在位置周围反弹。

\n

笔记:

\n
    \n
  1. .simultaneousGestureon nodeView 可以让我用一根手指在节点上同时拖动图形和节点。但我想用两个单独的手指拖动图形和节点:一个在节点上,另一个在图形上。
  2. \n
  3. 某些 UI(例如.offset上的ZStack)被设置为模仿我的应用程序的实际设置。
  4. \n
\n

如果有更好的方法,我也愿意在 UIKit 中执行此操作。我怀疑目前的问题是来自图形与节点手势处理程序上的竞争“触发周期”。

\n
struct StackOverflowTestView: View {\n    @State var graphOffset: CGFloat = .zero\n    @State var graphPreviousOffset: CGFloat = .zero\n\n    @State var nodePosition: CGFloat = 200.0\n    @State var nodePreviousPosition: CGFloat = 200.0\n\n    @State var nodeIsDragged = false\n\n    @State var lastNodeTranslation: CGFloat = .zero\n\n    var graphDrag: some Gesture {\n        DragGesture()\n            .onChanged { graphDragValue in\n                graphOffset = graphPreviousOffset + graphDragValue.translation.height\n\n                // If we\'re simultaneously dragging the node,\n                // add inverse graph translation to node\'s position,\n                // so that node stays under our finger:\n                if nodeIsDragged {\n                    nodePosition = nodePreviousPosition + lastNodeTranslation - graphDragValue.translation.height\n                }\n            }\n            .onEnded { _ in\n                graphPreviousOffset = graphOffset\n            }\n    }\n\n    var nodeDrag: some Gesture {\n        // .local: node translation is relative to graph\'s translation,\n        //    e.g. graph +200, node -200 = node translation is -400\n        // .global: node translation affected by graph\'s translation\n        DragGesture(coordinateSpace: SwiftUI.CoordinateSpace.global)\n            .onChanged { nodeDragValue in\n                nodeIsDragged = true\n                lastNodeTranslation = nodeDragValue.translation.height\n                nodePosition = nodePreviousPosition + nodeDragValue.translation.height\n            }\n            .onEnded { _ in\n                nodeIsDragged = false\n                lastNodeTranslation = 0\n                nodePreviousPosition = nodePosition\n            }\n    }\n\n    var nodeView: some View {\n        Rectangle()\n            .fill(.green.opacity(0.9))\n            .frame(width: 100, height: 100) // NODE SIZE\n            .position(x: 500, y: nodePosition) // NODE POSITION\n            .gesture(nodeDrag)\n    }\n\n    var graphView: some View {\n        ZStack { nodeView }\n            .border(.blue, width: 32)\n            .offset(x: 0, y: graphOffset) // GRAPH OFFSET\n            .background(Rectangle()\n                            .fill(.pink.opacity(0.1))\n                            .frame(width: 1200, height: 800)\n                            .gesture(graphDrag))\n    }\n\n    var body: some View {\n        graphView.border(.yellow)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Iam*_*ker 3

nodeDrag您需要将逆图翻译添加到-> DragGesture->内的节点位置.onChanged。您仅在内部添加graphDrag。

更改nodeDrag-> DragGesture-> .onChanged-> nodePosition = nodePreviousPosition + nodeDragValue.translation.height为

nodePosition = nodePreviousPosition + nodeDragValue.translation.height - (graphOffset - graphPreviousOffset)

仅供参考 -> 我重新排列以下方程以获得graphDragValue.translation.height ( graphOffset = graphPreviousOffset + graphDragValue.translation.height) 的值。如果你愿意,你可以使用一个单独的状态变量。


幽灵行为的解释

这就是您的代码所发生的情况。

当您移动 graphView 时,在内部graphDrag->.onChanged您将逆图平移添加到节点的位置。这是绝对正确的。所以这可以让你的nodeView保持在正确(相同)的位置。

但是如果你的手指在nodeView(绿色视图)上略有变化nodeDrag->.onChange方法 get 调用。在其中您正在设置一个nodePosition新值。在这里你忘记了你graphView已经搬家了。因此,如果 graphView 没有移动,nodeView 就会移动到它应该在的位置。

再次通过grapDrag->onChanged将nodeView移动到正确的位置。

所以这种情况不断发生,nodeView 不断在位置上弹跳。这会产生幽灵行为。

如果您将手指保持在 nodeView 上非常静止(这样就不会进行 onChange get 调用),您的代码将起作用。

按照我上面提到的更改代码即可解决问题。