在界面开发中,经常要使用到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
    }
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐