SwiftUI Wheel Picker如何在动画过程中实时更新选中值?
SwiftUI Wheel Picker: 逐值动画过渡与延迟值确认
问题分析
默认Wheel Picker滑动时会直接跳转到目标值,且选中状态实时更新。要实现数值逐帧递增/递减的动画,并确保未完成动画时保存旧值,需分离显示用的临时值与实际保存的最终值。
解决方案
通过双状态变量配合定时器逐帧更新,实现平滑的数值过渡,仅在动画结束后确认选中值:
struct RepPickerView: View { // 配置参数 let minValue = 10 let maxValue = 15 // 绑定Picker的显示值,用于动画过渡 @State private var displayedReps = 10 // 实际保存的最终值,仅在动画结束后更新 @State private var savedReps = 10 // 防止动画过程中重复触发的标记 @State private var isAnimating = false var body: some View { VStack(spacing: 20) { Picker(selection: $displayedReps, label: Text("Reps")) { ForEach(minValue...maxValue, id: \.self) { count in Text("\(count)") .foregroundColor(Color("lightGreenColor")) } } .pickerStyle(.wheel) // 明确指定滚轮样式 Button("保存") { // 仅使用savedReps执行保存逻辑 print("已保存数值:\(savedReps)") // 替换为你的实际保存代码 } .buttonStyle(.borderedProminent) } .padding() .onChange(of: displayedReps) { targetValue in // 过滤重复触发或动画中的操作 guard !isAnimating, targetValue != savedReps else { return } isAnimating = true let start = savedReps let step = targetValue > start ? 1 : -1 let totalSteps = abs(targetValue - start) // 逐帧更新数值,每步间隔0.1秒(可调整动画速度) for index in 1...totalSteps { DispatchQueue.main.asyncAfter(deadline: .now() + Double(index)*0.1) { displayedReps = start + step*index // 动画完成后同步保存值并重置标记 if index == totalSteps { savedReps = targetValue isAnimating = false } } } } } }
关键实现细节
- 双状态分离:
displayedReps负责Picker的视觉展示与动画,savedReps仅存储最终确认值,确保保存操作的准确性 - 逐值动画:通过
DispatchQueue.main.asyncAfter定时器,按固定间隔逐步更新显示值,实现10→11→12→…→15的连续过渡 - 动画冲突避免:
isAnimating标记防止用户在动画未完成时再次滑动Picker导致的逻辑混乱 - 保存逻辑安全:按钮仅读取
savedReps,动画过程中该值始终为旧值,完全符合需求
内容的提问来源于stack exchange,提问作者Alex Izmestyev
相关产品推荐
相关产品推荐

