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
相关产品推荐
相关产品推荐

