SwiftUI:使用不同的手指同时拖动父视图和子视图

q8l4jmvw  于 2022-10-31  发布在  Swift
关注(0)|答案(1)|浏览(193)

bounty将于明天到期。回答此问题可获得+500的声望奖励。adammenges正在寻找标准答案

我有一个应用程序,它有一个父视图(graphView,带有一个蓝色的粗边框)和一个子视图(nodeView,绿色框)。每个视图都有自己的SwiftUI DragGesture处理程序。我希望能够用不同的手指移动绿色节点和蓝色边框的图形。
目标:当我主动拖动“蓝边图形”时,保持“绿色节点”在我的手指下。
问题:绿色节点“幽灵”,即似乎在位置周围反弹。
注意事项:

  1. nodeView上的.simultaneousGesture可以让我用一个手指同时拖动图形和节点。但我想用两个手指分别拖动图形和节点:一个在节点上,另一个在图形上。
    1.一些UI(例如ZStack上的.offset)设置为模仿我的应用程序的实际设置。
    如果有更好的方法的话,我也愿意在UIKit中这样做。我怀疑目前的问题是来自图形和节点手势处理程序上的竞争“触发周期”。
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)
    }
}
huwehgph

huwehgph1#

你需要在nodeDrag-〉DragGesture-〉.onChanged内添加反向图形平移到节点的位置。你只在graphDrag内添加。
nodeDrag-〉DragGesture-〉.onChanged-〉nodePosition = nodePreviousPosition + nodeDragValue.translation.height更改为
nodePosition = nodePreviousPosition + nodeDragValue.translation.height - (graphOffset - graphPreviousOffset)
FYI -〉我重新排列了下面的等式,得到了graphDragValue.translation.heightgraphOffset = graphPreviousOffset + graphDragValue.translation.height)的值。如果你愿意,你可以使用一个单独的状态变量。

幽灵行为的解释

这就是你的代码所发生的事情。
当你移动graphView时,在graphDrag-〉.onChanged中,你添加了反向图形平移到节点的位置。这是绝对正确的。所以这使你的nodeView保持在正确的(相同的)位置。
但是如果你的手指在nodeView(绿色视图)上稍微改变了nodeDrag-〉.onChange方法get调用。在里面你设置了nodePosition一个新的值。这里你忘记了你的graphView已经移动了。所以如果graphView没有移动,nodeView会移动到它应该在的位置。
通过grapDrag-〉onChanged,节点视图将移动到正确的位置。
因此,这种情况不断发生,nodeView不断在各个位置反弹,这就产生了ghost行为。
如果您将手指保持在nodeView上非常静止(以便不进行onChange get调用),您的代码将工作。
按照我上面提到的那样修改代码就可以解决这个问题。

相关问题