SwiftUI WheelPicker滚动时无法更新绑定变量的问题求助
解决SwiftUI WheelPicker滚动时实时更新绑定值的问题
默认的SwiftUI WheelPicker确实只会在滚动停止后才触发绑定值更新,要实现苹果时钟App那样滚动过程中实时同步的效果,需要自己封装原生的UIPickerView并监听滚动中的选中事件,具体方案如下:
自定义实时更新的WheelPicker
通过UIViewRepresentable封装UIPickerView,利用其代理方法在滚动过程中实时更新绑定值:
struct WheelPicker: UIViewRepresentable { typealias UIViewType = UIPickerView @Binding var selectedValue: Int let values: [Int] func makeUIView(context: Context) -> UIPickerView { let picker = UIPickerView() picker.dataSource = context.coordinator picker.delegate = context.coordinator // 初始化选中行 picker.selectRow(values.firstIndex(of: selectedValue) ?? 0, inComponent: 0, animated: false) // 保持系统滚轮样式 picker.selectionIndicatorStyle = .white return picker } func updateUIView(_ uiView: UIPickerView, context: Context) { // 外部绑定值变化时同步更新Picker选中状态 let targetIndex = values.firstIndex(of: selectedValue) ?? 0 if uiView.selectedRow(inComponent: 0) != targetIndex { uiView.selectRow(targetIndex, inComponent: 0, animated: true) } } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate { let parent: WheelPicker init(_ parent: WheelPicker) { self.parent = parent } func numberOfComponents(in pickerView: UIPickerView) -> Int { 1 } func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { parent.values.count } // 自定义滚轮文字样式,匹配原SwiftUI样式 func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { let label = (view as? UILabel) ?? UILabel() label.text = "\(parent.values[row]) minutes" label.font = UIFont.preferredFont(forTextStyle: .body).withSize(UIFont.systemFontSize) label.textColor = .white label.textAlignment = .center return label } // 滚动过程中实时更新绑定值 func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { parent.selectedValue = parent.values[row] } } }
在ContentView中使用自定义Picker
替换原来的系统Picker,绑定minutes变量即可实现滚动时实时更新:
struct ContentView: View { @State private var minutes: Int = 1 private let minuteRange = Array(1..<120) var body: some View { VStack { // 使用自定义滚轮Picker WheelPicker(selectedValue: $minutes, values: minuteRange) .frame(height: 200) // 设置合适的滚轮高度 } .padding() .background(Color.black) // 方便查看白色文字 } }
核心原理
系统SwiftUI Picker为了优化性能,默认只在滚动结束后触发绑定更新;而原生UIPickerView的didSelectRow代理方法会在滚动过程中每一次选中行切换时触发,通过封装这个原生组件,就能实现像时钟App那样的实时同步效果。
内容的提问来源于stack exchange,提问作者Quppi
相关产品推荐
相关产品推荐

