You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 08:50:35