使用onTapGesture的Segmented Picker无点击响应问题求助
我明白你的困扰了——在Xcode 11 beta 5里,用Picker搭配SegmentedPickerStyle替代废弃的SegmentedControl后,只要加上onTapGesture,Picker就彻底没法切换选项了,但滚轮样式的Picker却能正常响应手势。这其实是手势事件优先级导致的问题:
问题根源
当你直接给设置了SegmentedPickerStyle的Picker添加onTapGesture时,这个手势会优先拦截所有点击事件,导致Picker本身的分段选择交互被完全覆盖,自然没法切换选中项了。而滚轮样式的Picker因为交互逻辑不同(点击更多是触发滚轮展开/调整,而非直接切换选项),所以手势和原生交互不会冲突。
解决方案:监听选择变化而非依赖点击手势
既然我们的需求是在选中项变化时执行逻辑,完全不需要借助点击手势——直接监听sel的状态变化就可以实现,而且能完美保留Picker的原生交互。这里有两种靠谱的实现方式:
方式1:自定义Binding
通过创建自定义的Binding,在设置选中值的同时执行你的逻辑,既保证Picker能正常切换选项,又能触发预期的操作:
import SwiftUI var oneSelected = false struct ContentView: View { @State private var sel = 0 // 自定义Binding,处理选择变化逻辑 private var selectionBinding: Binding<Int> { Binding( get: { self.sel }, set: { self.sel = $0 // 在这里执行原本要通过手势触发的逻辑 oneSelected = (self.sel == 1) } ) } var body: some View { VStack { Picker("Test", selection: $sel) { Text("A").tag(0) Text("B").tag(1) Text("C").tag(2) } .pickerStyle(SegmentedPickerStyle()) // 使用自定义Binding的Picker Picker("Test", selection: selectionBinding) { Text("A").tag(0) Text("B").tag(1) Text("C").tag(2) } .pickerStyle(SegmentedPickerStyle()) Text("Selected: \(sel)") } } } #if DEBUG struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } #endif
方式2:状态监听修饰符
如果你的Xcode 11 beta5版本支持onChange修饰符,也可以直接监听sel的变化:
Picker("Test", selection: $sel) { Text("A").tag(0) Text("B").tag(1) Text("C").tag(2) } .pickerStyle(SegmentedPickerStyle()) .onChange(of: sel) { newValue in oneSelected = (newValue == 1) }
要是beta版本不支持onChange,可以用onReceive结合Combine框架替代(需要先导入Combine):
import Combine // ... Picker("Test", selection: $sel) { Text("A").tag(0) Text("B").tag(1) Text("C").tag(2) } .pickerStyle(SegmentedPickerStyle()) .onReceive(Just(sel)) { newValue in oneSelected = (newValue == 1) }
方案优势
这种状态驱动的方式完全贴合SwiftUI的设计理念,不仅不会和原生控件的交互冲突,还能捕捉到所有选中项变化的场景(包括通过代码修改sel的情况,而手势只能响应用户点击的场景),比使用onTapGesture更可靠。
内容的提问来源于stack exchange,提问作者Michael Salmon

