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

SwiftUI问题:无法通过DragGesture修改视图大小

问题根源

问题出在手势的坐标空间会跟着父视图大小变化而乱掉。你拖动蓝色小方块修改父视图size的时候,父视图的frame一变,蓝色方块的位置也跟着挪。而DragGesture默认用的是本地坐标,每次布局变了,手势的初始位置就被重置,translation值会基于新位置重新计算,结果又触发size修改,无限循环下去直接把应用卡崩;加了动画虽然不崩,但坐标空间一直在变,translation肯定不对。

修复方案

核心就是让手势用固定的全局坐标空间,或者在手势刚启动时记下初始的全局位置,后面基于这个固定位置算偏移,别依赖视图本地坐标。

修复后的代码:

struct ResizableView: View {
    @State private var size: CGSize = CGSize(width: 100, height: 100)
    @State private var initialSize: CGSize?
    @State private var initialDragPos: CGPoint?

    var body: some View {
        ZStack {
            Color.red

            VStack {
                HStack {
                    Color.blue
                        .frame(width: 16, height: 16)
                        .gesture(resizeDragGesture)
                    Spacer()
                }
                Spacer()
            }
        }
        .frame(width: size.width, height: size.height)
        // 现在加动画也正常了
        .animation(.default, value: size)
    }

    private var resizeDragGesture: some Gesture {
        // 指定全局坐标空间,不受视图布局影响
        DragGesture(minimumDistance: 0, coordinateSpace: .global)
            .onChanged { value in
                guard let initialSize = initialSize, let startPos = initialDragPos else {
                    initialSize = size
                    initialDragPos = value.startLocation
                    return
                }
                // 计算和初始拖拽位置的真实偏移
                let deltaX = startPos.x - value.location.x
                let deltaY = startPos.y - value.location.y
                
                size = CGSize(
                    width: max(100, initialSize.width + deltaX),
                    height: max(100, initialSize.height + deltaY)
                )
            }
            .onEnded { _ in
                initialSize = nil
                initialDragPos = nil
            }
    }
}
关键改动
  • 给DragGesture加上coordinateSpace: .global,让手势基于全局坐标计算,不管父视图怎么变,坐标基准都不动
  • 新增initialDragPos记录手势刚启动时的全局位置,后面用当前位置和这个初始位置对比算偏移,不再直接用value.translation(因为translation会随视图布局重置)
  • 调整了size的计算逻辑,确保拖拽方向和视图大小变化匹配(比如向左拖,视图变宽)

改完之后,不会再无限循环,onEnded也能正常触发,加动画后拖拽逻辑完全符合预期。

内容的提问来源于stack exchange,提问作者Son Nguyen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 17:20:27