SwiftUI DragGesture拖动致应用冻结CPU占100%问题求解
问题根因
左右拖拽时应用冻结、CPU占用100%是无限布局重绘死循环导致,触发逻辑如下:
- 拖拽位移计算逻辑错误:
DragGesture的value.translation是手势从按下开始的累计总位移,不是相邻两次回调的增量位移,直接在原有width基础上累加总位移本身就会导致宽度计算异常 - 布局依赖形成闭环:拖拽把手(显示冒号的Text)放在ZStack的
bottomTrailing对齐位,ZStack宽度绑定width状态,width变化时把手会自动跟随ZStack的右侧边移动;而DragGesture的位移基于父视图坐标计算,哪怕手指按住不动,把手自身移动也会被识别为新的拖拽位移,触发onChanged再次修改width,直接形成「改宽度→把手位移→触发新拖拽→再改宽度」的死循环 - 显示宽度数值的Text直接绑定width设置frame,width变化时文本内容、布局尺寸同时刷新,进一步放大了布局连锁反应
问题复现代码
struct TestView2 : View { @State private var width : CGFloat = 400.0 var body : some View { VStack { ZStack(alignment: .bottomTrailing) { Text("\(width)") .frame(width: width) Text(":") .frame(width: 10, height: 30) .background(.bar) .gesture( DragGesture() .onChanged { value in width = max(100, width + value.translation.width) print(width) } ) } .frame(width: width, height: 30, alignment: .topLeading) .border(.gray, width: 1) .background(.green) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } }
修复方案
核心修改逻辑:
- 拖拽开始时记录初始宽度,用「初始宽度+手势总位移」直接计算当前宽度,不要做累加操作
- 拖拽结束后重置初始宽度标记,避免下次拖拽计算异常
- 解除显示文本和可变宽度的强frame绑定,避免不必要的布局连锁刷新
修复后可正常运行的代码:
struct TestView2 : View { @State private var width : CGFloat = 400.0 // 记录拖拽触发时的初始宽度 @State private var dragStartWidth: CGFloat = 0 var body : some View { VStack { ZStack(alignment: .bottomTrailing) { Text("\(width)") .padding(.horizontal) .frame(maxWidth: .infinity, alignment: .leading) Text(":") .frame(width: 10, height: 30) .background(.bar) .gesture( DragGesture() .onChanged { value in // 首次触发回调时记录起始宽度 if dragStartWidth == 0 { dragStartWidth = width } // 基于初始宽度计算当前宽度,设置最小宽度限制 width = max(100, dragStartWidth + value.translation.width) } .onEnded { _ in // 拖拽结束重置标记 dragStartWidth = 0 } ) } .frame(width: width, height: 30, alignment: .topLeading) .border(.gray, width: 1) .background(.green) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } }
参考截图


内容的提问来源于stack exchange,提问作者Nico
相关产品推荐
相关产品推荐

