SwiftUI:如何同时拖动父视图和子视图,但需要使用不同的手指进行操作。

9

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

目标:在我主动拖动“蓝色边框图形”的同时,将“绿色节点”保持在我的手指下。

问题:绿色节点“幽灵”,即似乎在位置上反弹。

注意:

  1. .simultaneousGesture 在nodeView上会让我使用单个手指在节点上同时拖动图形和节点。但是我想使用两个单独的手指拖动图形和节点:一个在节点上,另一个在图形上。
  2. 一些UI(例如ZStack上的 .offset )设置为模仿我的应用程序的实际设置。

如果有更好的方法,我也可以在UIKit中完成此操作。我怀疑问题当前来自于竞争的'firing cycles'在图形与节点手势处理程序之间。

struct StackOverflowTestView: View {
    @State var graphOffset: CGFloat = .zero
    @State var graphPreviousOffset: CGFloat = .zero

    @State var nodePosition: CGFloat = 200.0
    @State var nodePreviousPosition: CGFloat = 200.0

    @State var nodeIsDragged = false

    @State var lastNodeTranslation: CGFloat = .zero

    var graphDrag: some Gesture {
        DragGesture()
            .onChanged { graphDragValue in
                graphOffset = graphPreviousOffset + graphDragValue.translation.height

                // If we're simultaneously dragging the node,
                // add inverse graph translation to node's position,
                // so that node stays under our finger:
                if nodeIsDragged {
                    nodePosition = nodePreviousPosition + lastNodeTranslation - graphDragValue.translation.height
                }
            }
            .onEnded { _ in
                graphPreviousOffset = graphOffset
            }
    }

    var nodeDrag: some Gesture {
        // .local: node translation is relative to graph's translation,
        //    e.g. graph +200, node -200 = node translation is -400
        // .global: node translation affected by graph's translation
        DragGesture(coordinateSpace: SwiftUI.CoordinateSpace.global)
            .onChanged { nodeDragValue in
                nodeIsDragged = true
                lastNodeTranslation = nodeDragValue.translation.height
                nodePosition = nodePreviousPosition + nodeDragValue.translation.height
            }
            .onEnded { _ in
                nodeIsDragged = false
                lastNodeTranslation = 0
                nodePreviousPosition = nodePosition
            }
    }

    var nodeView: some View {
        Rectangle()
            .fill(.green.opacity(0.9))
            .frame(width: 100, height: 100) // NODE SIZE
            .position(x: 500, y: nodePosition) // NODE POSITION
            .gesture(nodeDrag)
    }

    var graphView: some View {
        ZStack { nodeView }
            .border(.blue, width: 32)
            .offset(x: 0, y: graphOffset) // GRAPH OFFSET
            .background(Rectangle()
                            .fill(.pink.opacity(0.1))
                            .frame(width: 1200, height: 800)
                            .gesture(graphDrag))
    }

    var body: some View {
        graphView.border(.yellow)
    }
}
1个回答

3

您需要在nodeDrag -> DragGesture -> .onChanged中添加反向图形翻译到节点位置。您只是在graphDrag中添加。

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

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

FYI -> 我重新排列了下面的方程式,以获得graphDragValue.translation.height的值(graphOffset = graphPreviousOffset + graphDragValue.translation.height)。如果您想要,可以使用单独的状态变量。


解释幽灵行为

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

当您移动graphView时,在graphDrag -> .onChanged中将反向图形平移添加到节点位置上。这是绝对正确的。因此,这使得您的nodeView保持在正确(相同)的位置。

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

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

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

如果您将手指放在nodeView上非常静止(使其不调用onChange),则您的代码将正常工作。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接