SwiftUI拖拽手势与contentTransition异常:拖拽时文本跳帧卡顿
问题原因及解决方案
问题根源
- 动画上下文冲突:在拖拽手势的
onChanged中用withAnimation包裹offset更新,会让整个视图进入动画状态,和文本的numericText过渡动画互相干扰,导致文本更新时跳帧卡顿。 - 冗余手势判断:原代码里对水平滑动的分支处理完全是无用逻辑,无意义的判断会增加额外计算开销。
修复步骤
- 移除
onDrag里的withAnimation:拖拽是实时交互操作,直接更新offset即可,不需要额外添加动画包裹 - 简化手势判断逻辑,砍掉无用的水平滑动分支
- (可选)给文本的数字过渡指定显式动画,避免和其他隐式动画冲突
修改后的完整代码
struct ContentView: View { @State var offset : CGFloat = 0 @State var time: String = "0.0" let timer = Timer.publish(every: 0.1, on: .main, in: .common).autoconnect() var body: some View { VStack { Spacer() Text(time) .contentTransition(.numericText()) .onReceive(timer) { value in // 指定显式动画,避免和其他动画冲突 withAnimation(.linear(duration: 0.1)) { time = value.timeIntervalSince1970.minuteSecond } } Spacer() } .frame(maxWidth: .infinity) .background(.red) .offset(y: offset) .gesture(swipeDownGesture) } } extension ContentView { var swipeDownGesture: some Gesture { DragGesture() .onChanged(onDrag(value:)) .onEnded(onSwipeDownEnded(value:)) } func onDrag(value: DragGesture.Value){ let verticalAmount = value.translation.height // 仅处理向下拖拽的情况,直接更新offset if verticalAmount > 0 { offset = verticalAmount } } func onSwipeDownEnded(value: DragGesture.Value){ withAnimation(.smooth(duration: 0.32, extraBounce: 0.22)) { offset = 0 } } }
额外说明
- 拖拽时实时更新
offset是原生交互反馈,不需要动画,动画只在拖拽结束的复位阶段添加即可 - 给文本数字过渡指定和定时器间隔匹配的线性动画,能让数字变化更平滑,避免和其他动画上下文冲突
内容的提问来源于stack exchange,提问作者Heshan
相关产品推荐
相关产品推荐

