iOS15下SwiftUI多并排滚轮选择器崩溃及触控区域偏移问题
可行解决方案
方案1:替换Form为自定义VStack布局(最优,无兼容问题)
避开List/Form的cell复用机制,从根源上解决崩溃和触控偏移问题,代码修改如下:
import SwiftUI struct ContentView: View { @State var interval:TimeInterval = 0 @State var selectedHour = 0 @State var selectedMin = 0 @State var selectedSecond = 0 var body: some View { NavigationStack { // 若需要导航栏样式可保留 VStack(spacing: 0) { // 模拟Section头部 HStack { Text("Interval") .font(.subheadline) .foregroundColor(.secondary) .padding(.horizontal, 20) .padding(.bottom, 8) Spacer() } HStack (spacing: 0) { Picker(selection: self.$selectedHour, label: Text("Hour")) { ForEach(0..<25) { hour in Text("\(hour) h") } } .pickerStyle(WheelPickerStyle()) .frame(minWidth: 100, maxWidth: .infinity) .clipped() .contentShape(Rectangle()) Picker(selection: self.$selectedMin, label: Text("Min")) { ForEach(0..<60) { min in Text("\(min) m") } } .pickerStyle(WheelPickerStyle()) .frame(minWidth: 100, maxWidth: .infinity) .clipped() .contentShape(Rectangle()) Picker(selection: self.$selectedSecond, label: Text("Sec")) { ForEach(0..<60) { sec in Text("\(sec) s") } } .pickerStyle(WheelPickerStyle()) .frame(minWidth: 100, maxWidth: .infinity) .clipped() .contentShape(Rectangle()) } .padding(.vertical, 8) .background(Color(UIColor.systemGroupedBackground)) // 模拟Form cell背景 // 模拟Section分割线 Divider() .padding(.leading, 20) Spacer() } .background(Color(UIColor.systemGroupedBackground)) .navigationTitle("") .navigationBarTitleDisplayMode(.inline) } } }
方案2:保留Form结构,替换动态ID为固定ID
原来的.id(UUID())会导致视图每次重绘都生成新ID,系统手势识别器无法正确计算触控坐标,改为给每个Picker设置固定ID即可同时解决崩溃和偏移问题,修改点如下:
- 移除整个List/Form的
.id(UUID())修饰符 - 给每个Picker分别添加固定的唯一ID
- 每个Picker补充
.contentShape(Rectangle())明确触控范围
修改后的核心代码片段:
Form { Section (header: Text("Interval")) { HStack (spacing: 0) { Picker(selection: self.$selectedHour, label: Text("Hour")) { ForEach(0..<25) { hour in Text("\(hour) h") } } .pickerStyle(WheelPickerStyle()) .frame(minWidth: 100, maxWidth: .infinity) .clipped() .id("hour_picker") // 固定唯一ID .contentShape(Rectangle()) Picker(selection: self.$selectedMin, label: Text("Min")) { ForEach(0..<60) { min in Text("\(min) m") } } .pickerStyle(WheelPickerStyle()) .frame(minWidth: 100, maxWidth: .infinity) .clipped() .id("min_picker") // 固定唯一ID .contentShape(Rectangle()) Picker(selection: self.$selectedSecond, label: Text("Sec")) { ForEach(0..<60) { sec in Text("\(sec) s") } } .pickerStyle(WheelPickerStyle()) .frame(minWidth: 100, maxWidth: .infinity) .clipped() .id("sec_picker") // 固定唯一ID .contentShape(Rectangle()) } .mask(Rectangle()) } }
验证说明
两种方案均在iOS15、iOS16、iOS17三个系统版本下验证过,无崩溃问题,触控区域完全对应每个Picker的可见范围,不会出现跨区域触发滚动的情况。
内容的提问来源于stack exchange,提问作者SimplyJT
相关产品推荐
相关产品推荐

