在界面开发中,经常要使用到DatePicker控件,如年月日,时分选择。
但我们还要修改它的显示方式,文字语言,及其他功能
先看下最简单的调用方法就是:
@State private var date = Date()
var body: some View {
DatePicker(
"Start Date",
selection: $date,
displayedComponents: [.date]
)
}
就是一个最简单的年月日选择控件,如图:
年月日时分显示时,不出现年份的解决方法
我们当然需要默认设置为当 年月日,时分都放在一起,想让用户一次性选择年月日,时,分,结果不行。
DatePicker( selection: $selectedDate, in: dateRange(), displayedComponents: [.date,.hourAndMinute]),啊!!!燚
因此需要分开两个DataPicker处理,如下
contentView: View {
@State private var selectedDate = Date()
var body: some View {
VStack {
Text("Selected date: \(selectedDate, formatter: dateFormatter)")
DatePicker( selection: $selectedDate, displayedComponents: [.date]){
Text("日期")
}
DatePicker(selection: $selectedDate, displayedComponents: [.hourAndMinute]) {
Text("时间")
}
}
}
显示效果如下:
滚动式控件的界面
就在DatePicker后面加上:.datePickerStyle(WheelDatePickerStyle()) 就可以。
显示效果如下:
DatePicker的中文化和国际化
那还有问题就是这个月份是英文的,我们需要修改为中文语言,就需要在DatePicker后面加上:.environment(.locale, Locale(identifier: "zh_CN"))。
当然如果是想国际化APP的话,就使用:.environment(\.locale, Locale.autoupdatingCurrent)
日历列表控件界面
还有一种显示效果是直接出现日期列表控件(不包括时分),如下
只需要把.datePickerStyle(WheelDatePickerStyle())修改.datePickerStyle(.graphical)就可以
以下是完整代码:
struct testView: View {
@State private var selectedDate = Date()
var body: some View {
VStack {
Text("Selected date: \(selectedDate, formatter: dateFormatter)")
DatePicker( selection: $selectedDate, displayedComponents: [.date]){ //添加一个DatePicker视图,将它和selectedDate属性进行绑定,并设置DatePicker的组件类型为小时和分钟
Text("日期")
}
.datePickerStyle(WheelDatePickerStyle())
.environment(\.locale, Locale.autoupdatingCurrent)
//.environment(.locale, Locale(identifier: "zh_CN")) //中文显示
DatePicker(selection: $selectedDate, displayedComponents: [.hourAndMinute]) { //添加一个DatePicker视图,将它和selectedDate属性进行绑定,并设置DatePicker的组件类型为小时和分钟
Text("时间")
}
.datePickerStyle(WheelDatePickerStyle())
}
}
private var dateFormatter: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .long
return formatter
}
}
日期设置范围
我们显示的日历列表,如果需要设置一个特定的日期范围,就可以,需要创建一个:ClosedRange<Date>,其实就是一个startDate...endDate,你可以先定义开始和结束日期:
let startDate = Calendar.current.date(from: DateComponents(year: 2023, month: 3, day: 1))!
let endDate = Calendar.current.date(from: DateComponents(year: 2023, month: 6, day: 30))!
然后调用
DatePicker( "",selection: $selectedDate,in: startDate...endDate,displayedComponents: [.date]),也可以定义一个ClosedRange<Date>
func dateRange() -> ClosedRange<Date> {
let startDate = dateFormatter.date(from: "20230301")!
let endDate = dateFormatter.date(from: "20230630")!
return startDate...endDate
}
再调用:
DatePicker( "",selection: $selectedDate,in: dateRange(),displayedComponents: [.date])
日期格式设置
我们代码中有一个DateFormatter,他就是格式化输出日期表达式,并用来显示的,你可以根据你们需要来定义
private var dateFormatter1: DateFormatter {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
return formatter
}
private var dateFormatter2: DateFormatter {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .long
return formatter
}
例1
let startDate = dateFormatter1.date(from: "20230301")! --这就比刚才那个
let startDate = Calendar.current.date(from: DateComponents(year: 2023, month: 3, day: 1))! 简单多了
例2
Text("Selected date: \(selectedDate, formatter: dateFormatter)")
显示为
日期排除
DatePicker设置一个日期范围(如20230301-20230630),但要排除其中20230302,20230303两天。目前DatePicker不支持排除,需要自己处理,我这有个不完美的方案,就是这个日期显示时,弹出提示,而没有实现为灰不能选择(下一步实现)
struct testView: View {
@State private var selectedDate = Date()
@State private var showInvalidDate = false
@State private var invalidDate: Date?
// 定义日期范围
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
return formatter
}()
var body: some View {
VStack {
DatePicker("选择日期", selection: $selectedDate, in: dateRange(), displayedComponents: .date)
.padding()
.onReceive([self.selectedDate].publisher.first()) { date in
if disabledDates().contains(where: { Calendar.current.isDate($0, inSameDayAs: date) }) {
self.invalidDate = date
self.showInvalidDate = true
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
self.showInvalidDate = false
}
DispatchQueue.main.async {
self.selectedDate = self.previousValidDate(for: date)
}
}
}
.datePickerStyle(GraphicalDatePickerStyle())
Text("所选日期:\(dateFormatter.string(from: selectedDate))")
.padding()
}
.alert(isPresented: $showInvalidDate) {
Alert(title: Text("不能选择"), message: Text("\(dateFormatter.string(from: invalidDate ?? selectedDate)) 无效。请重新选择日期。"), dismissButton: .default(Text("确定")))
}
}
func dateRange() -> ClosedRange<Date> {
let startDate = dateFormatter.date(from: "20230301")!
let endDate = dateFormatter.date(from: "20230630")!
return startDate...endDate
}
func disabledDates() -> [Date] {
let disabledDateStrings: [String] = ["20230302", "20230303"]
let disabledDates = disabledDateStrings.compactMap { dateFormatter.date(from: $0) }
return disabledDates
}
func previousValidDate(for date: Date) -> Date {
var previousDate = date
while disabledDates().contains(where: { Calendar.current.isDate($0, inSameDayAs: previousDate) }) {
previousDate = Calendar.current.date(byAdding: .day, value: -1, to: previousDate)!
}
return previousDate
}
}