SwiftUI中如何检测DragGesture当前位置对应的视图?
问题:检测DragGesture位置对应的视图并实现拖拽动作在视图间连续传递
我希望能够检测DragGesture位置下是否存在对应的视图。我曾尝试给每个视图(示例中的圆形)添加DragGesture,期望拖拽动作能在视图间连续传递,但并未成功。请问是否有可行的解决方法?

示例代码
import SwiftUI struct ContentView2: View { var body: some View { ZStack { Image(.background) .resizable() .scaledToFill() .ignoresSafeArea() .gesture( DragGesture(minimumDistance: 5) .onChanged { value in print ("DG detetected at \(value.location.x) : \(value.location.y)") }) HStack { Circle() .fill(.red) .frame(width: 100, height: 100) .gesture( DragGesture(minimumDistance: 0) .onChanged { value in print ("DG in red") }) Circle() .fill(.white) .frame(width: 100, height: 100) Circle() .fill(.blue) .frame(width: 100, height: 100) } } } } #Preview { ContentView2() }
解决方法
方法一:全局手势+GeometryReader位置检测
不在单个视图上绑定独立手势,而是给父容器添加全局拖拽手势,通过GeometryReader记录每个子视图的全局坐标范围,拖拽时实时判断当前落点属于哪个视图:
import SwiftUI struct ContentView2: View { // 存储各圆形的全局位置和尺寸 @State private var redCircleFrame: CGRect = .zero @State private var whiteCircleFrame: CGRect = .zero @State private var blueCircleFrame: CGRect = .zero var body: some View { ZStack { Image(.background) .resizable() .scaledToFill() .ignoresSafeArea() .gesture( DragGesture(minimumDistance: 0) .onChanged { value in let location = value.location // 匹配当前拖拽位置对应的视图 if redCircleFrame.contains(location) { print("拖拽在红色圆形上") } else if whiteCircleFrame.contains(location) { print("拖拽在白色圆形上") } else if blueCircleFrame.contains(location) { print("拖拽在蓝色圆形上") } else { print("拖拽在背景上:\(location.x) : \(location.y)") } } ) HStack(spacing: 0) { Circle() .fill(.red) .frame(width: 100, height: 100) .background( GeometryReader { proxy in Color.clear .onAppear { redCircleFrame = proxy.frame(in: .global) } .onChange(of: proxy.frame(in: .global)) { redCircleFrame = $0 } } ) Circle() .fill(.white) .frame(width: 100, height: 100) .background( GeometryReader { proxy in Color.clear .onAppear { whiteCircleFrame = proxy.frame(in: .global) } .onChange(of: proxy.frame(in: .global)) { whiteCircleFrame = $0 } } ) Circle() .fill(.blue) .frame(width: 100, height: 100) .background( GeometryReader { proxy in Color.clear .onAppear { blueCircleFrame = proxy.frame(in: .global) } .onChange(of: proxy.frame(in: .global)) { blueCircleFrame = $0 } } ) } } } } #Preview { ContentView2() }
方法二:simultaneousGesture实现手势并行响应
如果需要保留子视图的独立手势逻辑,使用simultaneousGesture替代默认的gesture,避免子视图拦截父视图手势,实现拖拽时的连续响应:
import SwiftUI struct ContentView2: View { @State private var currentActiveCircle: String? var body: some View { ZStack { Image(.background) .resizable() .scaledToFill() .ignoresSafeArea() .gesture( DragGesture(minimumDistance: 0) .onChanged { value in print("拖拽位置:\(value.location.x) : \(value.location.y)") } ) HStack(spacing: 0) { Circle() .fill(.red) .frame(width: 100, height: 100) .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { _ in currentActiveCircle = "red" print("拖拽在红色圆形上") } ) Circle() .fill(.white) .frame(width: 100, height: 100) .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { _ in currentActiveCircle = "white" print("拖拽在白色圆形上") } ) Circle() .fill(.blue) .frame(width: 100, height: 100) .simultaneousGesture( DragGesture(minimumDistance: 0) .onChanged { _ in currentActiveCircle = "blue" print("拖拽在蓝色圆形上") } ) } } } } #Preview { ContentView2() }
说明
- 方法一适合需要精确匹配拖拽落点的场景,能实时定位当前操作的视图;
- 方法二适合保留子视图独立手势逻辑的场景,通过并行手势避免拦截,实现拖拽时的连续触发。
内容的提问来源于stack exchange,提问作者Bartender1382
相关产品推荐
相关产品推荐

