SwiftUI DatePicker点击预先选中的日期时如何触发自动收起?
实现方案
SwiftUI 自带的graphical样式DatePicker默认仅在绑定的selection值发生变更时才会触发状态回调,点击已选中日期时绑定值无变化,因此不会触发自带的收起逻辑。可以通过叠加低优先级点击手势实现需求,具体实现如下:
1. 新增日期按天比较的扩展
先添加便捷方法判断两个日期是否为同一天,避免时分秒差异导致判断错误:
extension Date { func isSameDay(as otherDate: Date) -> Bool { Calendar.current.isDate(self, inSameDayAs: otherDate) } }
2. 改造DatePicker视图
给DatePicker添加同时识别的点击手势,手势优先级低于DatePicker原生手势,不会干扰正常的日期切换逻辑:
struct DatePickerView: View { @Binding var date: Date // 传入控制Picker显示的绑定值,也可以用系统环境的dismiss变量替代 @Binding var isPresented: Bool // 临时存储上次选中的日期,用于判断是否点击了已选日期 @State private var lastSelectedDate: Date = Date() var body: some View { DatePicker( "Select Date", selection: $date, in: ...Date(), displayedComponents: [.date] ) .datePickerStyle(.graphical) .background(Color.white) .cornerRadius(8) .padding(.horizontal, 40) .onAppear { // 页面出现时记录初始选中的日期 lastSelectedDate = date } .onChange(of: date) { newDate in // 日期变更时直接收起,和原有逻辑一致 if !newDate.isSameDay(as: lastSelectedDate) { isPresented = false } } // 叠加低优先级点击手势,优先让原生Picker处理日期选择 .simultaneousGesture( TapGesture() .onEnded { _ in // 点击后判断如果日期和上次选中的一致,说明点击了已选日期,手动收起 if date.isSameDay(as: lastSelectedDate) { isPresented = false } // 更新上次选中日期 lastSelectedDate = date } ) } }
3. 父视图调用示例
struct ParentView: View { @State private var selectedDate = Date() @State private var showDatePicker = false var body: some View { Button { showDatePicker = true } label: { Text(selectedDate.formatted(date: .abbreviated, time: .omitted)) } .sheet(isPresented: $showDatePicker) { DatePickerView(date: $selectedDate, isPresented: $showDatePicker) } } }
注意事项
- 如果你的DatePicker是放在
popover而非sheet中,替换isPresented的绑定值即可,逻辑完全通用 - 如果不需要保留跨组件的状态,也可以直接用
@Environment(\.dismiss) private var dismiss替代isPresented绑定,在需要收起的地方调用dismiss()即可
内容的提问来源于stack exchange,提问作者joern
相关产品推荐
相关产品推荐

