如何在堆叠视图之外开始拖动事件时获取拖动手势?

huangapple go评论60阅读模式
英文:

How to get a drag gesture when a drag event starts out of the stack view?

问题

如何在拖动操作在堆栈视图之外开始时检测堆栈视图的拖动事件?

屏幕上放置了主堆栈/视图(绿色)和子堆栈/视图(红色)。我需要实现的功能是检测GREEN视图的滑动/拖动交互。这里有两种情况,第二种情况失败了:

失败 成功
当拖动手势在RED视图之外(在GREEN视图上)开始,然后在RED视图上滑动时,无法获取RED视图的onChange事件。 当拖动手势在RED视图上开始时,可以获取onChange事件并且可以追踪拖动手势。
如何在堆叠视图之外开始拖动事件时获取拖动手势? 如何在堆叠视图之外开始拖动事件时获取拖动手势?

当拖动在堆栈视图之外开始时,如何检测拖动事件?

我在过去的一周里尝试了许多方法。其中一些方法包括:

  • 尝试为拖动坐标和某些视图设置坐标空间。
  • 尝试添加多个同时发生的手势识别器。

可以添加到Xcode Playgrounds或演示应用程序的示例代码。

import PlaygroundSupport
import SwiftUI
PlaygroundPage.current.needsIndefiniteExecution = true

struct DemoView: View {
    var body: some View {
        VStack {
            VStack {
                Text("3")
            }
            .id("RED")
            .frame(width: 200, height: 300, alignment: .center)
            .background(Color.red)
            .simultaneousGesture(
                DragGesture()
                    .onChanged({
                        print("🔴", $0.location)
                    })
            )
        }
        .id("GREEN")
        .frame(width: 400, height: 700, alignment: .center)
        .background(Color.green)
        .simultaneousGesture(
            DragGesture()
                .onChanged({
                    print("🟢", $0.location)
                })
        )
    }
}

// 写入绿色矩形 

let view =  DemoView()
PlaygroundPage.current.setLiveView(view)
英文:

How to detect drag events for the stack when drag action have started outside of the stack view?

On the screen are placed the main stack/view (GREEN) and sub-stack/view (RED). I need to implement functionality which detects swipe/drag interaction for the GREEN view. Here are two scenarios and the second is failing:

FAILING OK
When the drag gesture starts outside (on the GREEN view) of the RED view and then swiping over RED view onChange event for RED view can not be retrieved. When the drag gesture starts on the RED view onChange events are retrieved and drag gesture can be tracked.
如何在堆叠视图之外开始拖动事件时获取拖动手势? 如何在堆叠视图之外开始拖动事件时获取拖动手势?

How to detect drag events when drag have started outside of the stack view?

I have tried many things over the week. Some of them are:

  • I have tried setting coordinate space for drag coordinates, and some views.
  • Tried adding multiple simultaneous gesture recognizers.

Sample code which can be added to Xcode Playgrounds or demo app.

import PlaygroundSupport
import SwiftUI
PlaygroundPage.current.needsIndefiniteExecution = true

struct DemoView: View {
    var body: some View {
        VStack {
            VStack {
                Text("3")
            }
            .id("RED")
            .frame(width: 200, height: 300, alignment: .center)
            .background(.red)
            .simultaneousGesture(
                DragGesture()
                    .onChanged({
                        print("🔴", $0.location)
                    })
            )
        }
        .id("GREEN")
        .frame(width: 400, height: 700, alignment: .center)
        .background(.green)
        .simultaneousGesture(
            DragGesture()
                .onChanged({
                    print("🟢", $0.location)
                })
        )
    }
}

// Write grean rectangle 

let view =  DemoView()
PlaygroundPage.current.setLiveView(view)

答案1

得分: 2

  1. 为所有视图创建一个容器,使它们具有相同的坐标系统
  2. 在该容器中为所有所需视图找到框架
  3. 如果拖动位置位于该坐标系统中所需框架内,则执行操作
英文:
  1. Make a container to have a same coordinate system for all views
  2. Find frames for all desired views in that container
  3. Perform actions if the drag location is inside the desired frame in that coordinate system
struct DemoView: View {
    @State private var greenFrame: CGRect = .zero
    @State private var redFrame: CGRect = .zero

    var body: some View {
        ZStack {
            Color.green
                .frame(width: 400, height: 400, alignment: .center)
                .background { GeometryReader { p in
                    Spacer().onAppear() { greenFrame = p.frame(in: .named("CONTAINER")) }
                }}
            Color.red
                .frame(width: 200, height: 200, alignment: .center)
                .background { GeometryReader { p in
                    Spacer().onAppear { redFrame = p.frame(in: .named("CONTAINER")) }
                }}
        }
        .coordinateSpace(name: "CONTAINER")
        .gesture(DragGesture()
            .onChanged {
                if redFrame.contains($0.location) { print("🔴", $0.location) }
                if greenFrame.contains($0.location) { print("🟢", $0.location) }
            }
        )
    }
}

答案2

得分: 1

Using an overlay in this version it detects red drag [exclusively] if you start in red and green [also exclusively] if you start in green.

import SwiftUI

struct ContentView: View {
  var body: some View {
    VStack {
        DemoView()
    }
    .padding()
  }
}

struct DemoView: View {
  var body: some View {
    VStack {
        
    }
    .id("GREEN")
    .frame(width: 400, height: 700, alignment: .center)
    .background(Color.green)
    .simultaneousGesture(
        DragGesture(minimumDistance: 0)
            .onChanged({
                print("🟥", $0.location)
            })
    ).overlay {
        VStack {
            Text("3")
        }
        .id("RED")
        .frame(width: 200, height: 300, alignment: .center)
        .background(Color.red)
        .simultaneousGesture(
            DragGesture()
                .onChanged({
                    print("🟢", $0.location)
                })
        )
    }
  }
}

So if I comment out the green gesture and use the coordinates returned, I can use the coordinates returned to work out if I am in red or green within the "red" drag gesture.

.simultaneousGesture(
    DragGesture()
        .onChanged({
            if $0.location.x < 0 ||
                $0.location.y < 0 ||
                $0.location.x > 200 ||
                $0.location.y > 300 {
                print("🟥", $0.location)
            } else {
                print("🟢", $0.location)
            }
        })
)

Of course, if you start within the green, nothing is reported. But start within the red, and you get both.

You can now uncomment the green gesture and use the same sort of logic, within its gesture.

.simultaneousGesture(
    DragGesture(minimumDistance: 0)
        .onChanged({
            if ($0.location.x < 300 && $0.location.x > 100) {
                if ($0.location.y < 500 && $0.location.y > 200 ) {
                    print("🟢", $0.location)
                } else {
                    print("🟥", $0.location)
                }
            } else {
                print("🟥", $0.location)
            }
        })
)

And there you have it; if you start on green it will work, and if you start on red it will work.

英文:

Using an overlay in this version it detects red drag [exclusively] if you start in red and green [also exclusively] if you start in green.

import SwiftUI

struct ContentView: View {
  var body: some View {
    VStack {
        DemoView()
    }
    .padding()
  }
}

struct DemoView: View {
  var body: some View {
    VStack {
        
    }
    .id(&quot;GREEN&quot;)
    .frame(width: 400, height: 700, alignment: .center)
    .background(.green)
    .simultaneousGesture(
        DragGesture(minimumDistance: 0)
            .onChanged({
                print(&quot;&#128994;&quot;, $0.location)
            })
    ).overlay {
        VStack {
            Text(&quot;3&quot;)
        }
        .id(&quot;RED&quot;)
        .frame(width: 200, height: 300, alignment: .center)
        .background(.red)
        .simultaneousGesture(
            DragGesture()
                .onChanged({
                    print(&quot;&#128308;&quot;, $0.location)
                })
        )
    }
  }
}

So if I comment out the green gesture and use the coordinates returned, I can use the coordinates returned to work out if I am in red or green within the "red" drag gesture.

          .simultaneousGesture(
            DragGesture()
                .onChanged({
                    if $0.location.x &lt; 0 ||
                        $0.location.y &lt; 0 ||
                        $0.location.x &gt; 200 ||
                        $0.location.y &gt; 300 {
                        print(&quot;&#128994;&quot;, $0.location)
                    } else {
                        print(&quot;&#128308;&quot;, $0.location)
                    }
                })
        )

Of course, if you start within the green, nothing is reported. But start within the red, and you get both.

You can now uncomment the green gesture and use the same sort of logic, within its gesture.

    .simultaneousGesture(
        DragGesture(minimumDistance: 0)
            .onChanged({
                if ($0.location.x &lt; 300 &amp;&amp; $0.location.x &gt; 100) {
                    if ($0.location.y &lt; 500 &amp;&amp; $0.location.y &gt; 200 ) {
                        print(&quot;&#128308;&quot;, $0.location)
                    } else {
                        print(&quot;&#128994;&quot;, $0.location)
                    }
                } else {
                    print(&quot;&#128994;&quot;, $0.location)
                }
            })
    )

And there you have it; if you start on green it will work and if you start on red it will work.

huangapple
  • 本文由 发表于 2023年5月15日 05:19:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/76249732.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定