SwiftUI DatePicker实战:打造一个旅行计划App(含完整代码)

张开发
2026/4/12 16:26:10 15 分钟阅读

分享文章

SwiftUI DatePicker实战:打造一个旅行计划App(含完整代码)
SwiftUI DatePicker实战构建旅行计划App的进阶技巧每次规划旅行时最让人头疼的莫过于安排行程日期。作为iOS开发者我们可以用SwiftUI的DatePicker组件为用户打造流畅的日期选择体验。不同于基础教程本文将带你深入实战通过构建一个功能完善的旅行计划App掌握DatePicker的高级应用技巧。1. 旅行App的日期选择架构设计在开始编码前合理的架构设计能避免后期大量重构。旅行App通常需要处理三类日期时间数据出发日期用户旅程开始的日期返回日期旅程结束的日期必须晚于出发日期出发时间具体出发时刻struct TravelPlan: Identifiable { let id UUID() var departureDate: Date var returnDate: Date var departureTime: Date var destination: String }状态管理方案对比方案优点缺点适用场景State简单直接难以跨视图共享单一视图简单状态ObservedObject可复用需要额外创建类中等复杂度组件EnvironmentObject全局访问过度使用会导致依赖混乱应用级共享状态对于我们的旅行计划App采用State与Binding的组合最为合适struct TravelPlanView: View { State private var departureDate Date() State private var returnDate Calendar.current.date(byAdding: .day, value: 3, to: Date())! State private var departureTime Date() // 日期范围限制 private let minDate Date() private let maxDate Calendar.current.date(byAdding: .month, value: 6, to: Date())! }2. 智能日期范围限制实现基础日期范围限制使用in参数即可实现但旅行场景需要更智能的逻辑DatePicker(出发日期, selection: $departureDate, in: minDate...maxDate, displayedComponents: .date) .onChange(of: departureDate) { newDate in // 自动调整返回日期确保不早于出发日 if returnDate newDate { returnDate newDate } }进阶技巧节假日自动标记func isHoliday(_ date: Date) - Bool { let calendar Calendar.current let components calendar.dateComponents([.month, .day], from: date) // 示例节假日判断实际应使用更完整的节假日库 return (components.month 1 components.day 1) || // 元旦 (components.month 10 components.day 1) // 国庆 } DatePicker(出发日期, selection: $departureDate) .datePickerStyle(.graphical) .environment(\.calendar, Calendar.current) .background { if isHoliday(departureDate) { Color.orange.opacity(0.2) } }3. 多样式DatePicker的混合应用不同场景适合不同的DatePicker样式我们的旅行App可以这样组合出发日期选择- 图形日历式Section(header: Text(行程日期)) { DatePicker(出发日期, selection: $departureDate, displayedComponents: .date) .datePickerStyle(.graphical) DatePicker(返回日期, selection: $returnDate, in: departureDate...maxDate, displayedComponents: .date) .datePickerStyle(.graphical) }出发时间选择- 滚轮式Section(header: Text(出发时间)) { DatePicker(选择时间, selection: $departureTime, displayedComponents: .hourAndMinute) .datePickerStyle(.wheel) .labelsHidden() }快速选择预设- 紧凑式HStack { Text(行程时长) Spacer() DatePicker(, selection: $returnDate, in: departureDate...maxDate, displayedComponents: .date) .datePickerStyle(.compact) }4. 实时响应与数据联动优秀的日期选择体验应该实时反馈用户选择// 计算行程天数 var tripDuration: Int { Calendar.current.dateComponents([.day], from: departureDate, to: returnDate).day! 1 } // 在界面中显示 Section(header: Text(行程摘要)) { LabeledContent(出发日期, value: departureDate.formatted(.dateTime.day().month().year())) LabeledContent(返回日期, value: returnDate.formatted(.dateTime.day().month().year())) LabeledContent(出发时间, value: departureTime.formatted(.dateTime.hour().minute())) LabeledContent(行程天数, value: \(tripDuration)天) }日期变化时的副作用处理.onChange(of: departureDate) { [departureDate] newDate in let daysAdded Calendar.current .dateComponents([.day], from: departureDate, to: newDate) .day! // 保持返回日期与出发日期的相对间隔 returnDate Calendar.current .date(byAdding: .day, value: daysAdded, to: returnDate)! // 自动调整到合理时间范围 if returnDate maxDate { returnDate maxDate } }5. 性能优化与用户体验提升当日期选择器频繁更新时需要注意性能问题防抖处理State private var debounceTimer: Timer? DatePicker(出发日期, selection: $departureDate) .onChange(of: departureDate) { newDate in debounceTimer?.invalidate() debounceTimer Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in // 实际处理逻辑 updateRelatedData(for: newDate) } }大日期范围优化// 对于长期行程规划使用月份导航更高效 DatePicker(出发日期, selection: $departureDate, in: minDate...maxDate, displayedComponents: .date) .datePickerStyle(.graphical) .environment(\.calendar, Calendar(identifier: .gregorian))无障碍支持DatePicker(出发日期, selection: $departureDate) .accessibilityLabel(选择出发日期) .accessibilityHint(当前选择: \(departureDate.formatted())) .accessibilityAction(named: 今天) { departureDate Date() }6. 完整实现与扩展功能将上述功能整合成完整视图struct TravelPlanView: View { State private var departureDate Date() State private var returnDate Calendar.current.date(byAdding: .day, value: 3, to: Date())! State private var departureTime Date() State private var destination private let minDate Date() private let maxDate Calendar.current.date(byAdding: .month, value: 6, to: Date())! var body: some View { NavigationStack { Form { destinationSection dateSelectionSection timeSelectionSection summarySection } .navigationTitle(旅行计划) .toolbar { Button(保存) { savePlan() } } } } private var destinationSection: some View { Section(header: Text(目的地)) { TextField(输入旅行目的地, text: $destination) } } private var dateSelectionSection: some View { Section(header: Text(行程日期)) { DatePicker(出发日期, selection: $departureDate, in: minDate...maxDate, displayedComponents: .date) .datePickerStyle(.graphical) DatePicker(返回日期, selection: $returnDate, in: departureDate...maxDate, displayedComponents: .date) .datePickerStyle(.graphical) } } private var timeSelectionSection: some View { Section(header: Text(出发时间)) { DatePicker(选择时间, selection: $departureTime, displayedComponents: .hourAndMinute) .datePickerStyle(.wheel) } } private var summarySection: some View { Section(header: Text(行程摘要)) { LabeledContent(目的地, value: destination.isEmpty ? 未设置 : destination) LabeledContent(出发日期, value: departureDate.formatted(.dateTime.day().month().year())) LabeledContent(返回日期, value: returnDate.formatted(.dateTime.day().month().year())) LabeledContent(出发时间, value: departureTime.formatted(.dateTime.hour().minute())) LabeledContent(行程天数, value: \(tripDuration)天) } } private var tripDuration: Int { Calendar.current.dateComponents([.day], from: departureDate, to: returnDate).day! 1 } private func savePlan() { let newPlan TravelPlan( departureDate: departureDate, returnDate: returnDate, departureTime: departureTime, destination: destination ) // 保存逻辑... } }扩展功能建议添加重复行程功能每周/每月固定出行集成天气API显示选定日期的天气预报实现多城市行程规划每个城市可设置不同日期添加分享功能生成行程图片分享给同伴

更多文章