Date 是一种特有的日期数据类型。包含年月日,时分秒数据。
// 声明方法: now 代表当前日期时间
var wakeUp = Date.now
// 声明方法: distantPast 代表至少 2000 年以前
var wakeUp = Date.distantPast
Date 类型支持加减运算,其单位是秒。
var actualSleep = 48880.0
let sleepTime = wakeUp - actualSleep
// 2023-11-27 12:57:16 +0000
print(sleepTime)
Date 格式用文本展示时,经常需要用到格式化器。
private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd"
formatter.locale = Locale(identifier: "en_US")
formatter.calendar = Calendar(identifier: .gregorian)
formatter.timeZone = TimeZone.current
return formatter
}()
如果需要支持多语言,最好使用 setLocalizedDateFormatFromTemplate
方法
// 声明格式化器
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.setLocalizedDateFormatFromTemplate("MMMd")
return formatter
}()
// 不同模板的效果(以美国英语为例)
// formatter.setLocalizedDateFormatFromTemplate("MMMd") // Oct 31
// formatter.setLocalizedDateFormatFromTemplate("MMMMd") // October 31
// formatter.setLocalizedDateFormatFromTemplate("yMMMd") // Oct 31, 2024
// formatter.setLocalizedDateFormatFromTemplate("yMMMMd") // October 31, 2024
// formatter.setLocalizedDateFormatFromTemplate("Md") // 10/31
// formatter.setLocalizedDateFormatFromTemplate("yMd") // 10/31/24
// 同一个模板在不同地区的显示效果
// "MMMd" 模板在不同 locale 下的显示:
// formatter.locale = Locale(identifier: "en_US") // Oct 31
// formatter.locale = Locale(identifier: "zh_CN") // 10月31日
// formatter.locale = Locale(identifier: "fr_FR") // 31 oct.
// formatter.locale = Locale(identifier: "de_DE") // 31. Okt
// formatter.locale = Locale(identifier: "ja_JP") // 10月31日
// 使用时
Text(dateFormatter.string(from: date))
Text(date, formatter: dateFormatter)
Swift 提供了 Date
处理日期的功能,它封装了年、月、日、时、分、秒、时区等。然而,我们不想考虑其中的大部分,我们想说“给我一个早上 8 点的起床时间,不管今天是哪一天。
为此,Swift 提供了一个类型称为 DateComponents
,它允许我们读取或写入日期的特定部分,而不是整个日期。DateComponents
会将日期所需的所有部分存储为单独的值,这意味着我们可以读取小时、分钟部分,而忽略其余部分。然后我们需要做的就是将分钟乘以 60(得到秒而不是分钟),将小时乘以 60 和 60(得到秒而不是小时)。
如果想要设置一个今天上午 8 点的日期,可以通过 DateComponents
进行具体设置。
//1. 创建一个 DateComponents 对象
var components = DateComponents()
//2. 按需要设置 DateComponents 对象的具体属性
components.hour = 8
components.minute = 0
//3. 使用 Calendar 的方法,传入 DateComponents 参数,生成 Date
let date = Calendar.current.date(from: components)
//由于日期验证的困难,date(from:) 方法实际上返回一个Optional的值
//因此最好使用 nil 合并来表示“如果失败,只需将当前日期还给我”,如下所示:
let date = Calendar.current.date(from: components) ?? .now
我们还可以要求 iOS 提供特定日期的 DateComponents
,然后读出其中具体的属性。我们会返回一个实例,其中包含其所有属性的可选值。虽然我们知道小时和分钟会在那里(因为这些是我们要求的),但仍然需要解开可选值或提供默认值。
//使用 Calendar 的方法,传入 Date 类型给 from 参数,生成 dateComponents
let components = Calendar.current.dateComponents([.hour, .minute], from: someDate)
//用点语法读取 dateComponents 的属性
let hour = components.hour ?? 0
let minute = components.minute ?? 0
依靠非常有效的 format
参数,可以读取我们想要显示日期的任何部分。
//例如,如果我们只想要一个Date的时间,可以这样写:
Text(Date.now, format: .dateTime.hour().minute())
//或者,如果我们想要日、月和年,我们会这样写:
Text(Date.now, format: .dateTime.day().month().year())
//您可能想知道程序如何适应处理不同的日期格式——例如,在英国使用日/月/年,但在其他一些国家/地区,使用月/日/年。神奇的是,我们不需要担心这一点:当我们编写 day().month().year() 时,我们要求的是该数据,而不是排列它,iOS 将使用用户的偏好自动格式化该数据。
//作为替代方案,我们可以直接在日期上使用该 formatted() 方法,传入配置选项,用于格式化日期和时间,如下所示:
Text(Date.now.formatted(date: .long, time: .shortened))
DatePicker 日历选择器 是一个收缩型视图。
//1. 直接使用字符串作为 label
DatePicker(
"Label",
selection: $nextFullMoonDate
)
//2. 使用 label 视图
DatePicker(
selection: $nextFullMoonDate,
label: {
Text("Label")
}
)
**隐藏 label :**如果直接将 DatePicker 的 label 位置的字符串,设置为空。虽然能起到隐藏 label 的作用,但是有两个问题
因此更好的替代方法是使用 labelsHidden()
修饰符,如下所示:
DatePicker("Please enter a date", selection: $wakeUp)
.labelsHidden()
使用 DatePicker 时,需要绑定 Date 类型的状态参数。
//需要先设定一个用于绑定的Date类型参数
@State private var nextFullMoonDate = Date()
@State private var nextFullMoonDate = Date.now
DatePicker(
selection: $nextFullMoonDate,
label: { Text("Label") }
)
通过 displayedComponents
参数,可以设置日期选择器的显示形式。
// 1.默认:如果不提供此参数,用户将看到天、小时和分钟
DatePicker("",
selection: $arrivalDate,
displayedComponents: [.date, .hourAndMinute]
)
// 2.如果使用 .date ,用户会看到月、日和年
DatePicker("",
selection: $theDateAndTime,
displayedComponents: .date
)
// 3.如果使用 .hourAndMinute ,用户只能看到小时和分钟组件
DatePicker("",
selection: $justTime,
displayedComponents: .hourAndMinute
)
in
参数的工作方式与 Stepper
类似。我们可以为其提供一个日期范围,日期选择器将确保用户无法选择超出该范围的日期范围。
//支持从某一天开始选到永远
@State private var arrivalDate = Date()
let fromToday = Calendar.current.date(byAdding: .minute, value: -1, to: Date())!
DatePicker("",
selection: $arrivalDate,
in: fromToday...,
displayedComponents: .date
)
//支持选到某一天截止
@State private var arrivalDate = Date()
DatePicker("",
selection: $arrivalDate,
in: ...Date(),
displayedComponents: [.date, .hourAndMinute]
)
//还可以制定一个具体范围,传入给 in 使用
//例如:创建今天到明天一天
func exampleDates() {
// 根据距离第一个时间点的具体时间,创建第二个时间点(单位为秒)
let tomorrow = Date.now.addingTimeInterval(86400)
// 创建一个时间范围 range
let range = Date.now...tomorrow
}
//例如:创建今天到未来30天的范围
//通过计算属性设定一个范围(高级)
var withinNext30Days: ClosedRange<Date> {
let today = Calendar.current.date(byAdding: .minute, value: -1, to: Date())!
let next30Days = Calendar.current.date(byAdding: .day, value: 30, to: Date())!
return today...next30Days
}
DatePicker("",
selection: $nextFullMoonDate,
in: withinNext30Days
)
从 iOS 14 开始,您可以使用新的 GraphicalDatePickerStyle()
修饰符来获得高级日期选择器,该选择器显示日历以及用于输入精确时间的空格:
struct ContentView: View {
@State private var date = Date.now
var body: some View {
VStack {
Text("Enter your birthday")
.font(.largeTitle)
DatePicker("Enter your birthday", selection: $date)
.datePickerStyle(GraphicalDatePickerStyle())
.frame(maxHeight: 400)
}
}
}
MultiDatePicker
显示一个日历视图,用户可以在其中同时选择多个日期,无论是从任何可能的日期还是从您选择的日期范围。
// 在最简单的形式中,您只需要某种状态来跟踪他们选择的日期,然后将其绑定到您的选择器:
struct ContentView: View {
@State var dates: Set<DateComponents> = []
var body: some View {
MultiDatePicker("Select your preferred dates", selection: $dates)
}
}
但是,您很可能想要将这些日期组件转换为实际日期,在这种情况下,您需要从环境中读取用户的日历并根据需要转换数据:
struct ContentView: View {
@Environment(\\.calendar) var calendar
@State var dates: Set<DateComponents> = []
var body: some View {
VStack {
MultiDatePicker("Select your preferred dates", selection: $dates)
Text(summary)
}
.padding()
}
// 把选择的日期集合,转换成字符串
var summary: String {
dates.compactMap { components in
calendar.date(from: components)?.formatted(date: .long, time: .omitted)
}.formatted()
}
}
默认情况下,用户可以选择他们喜欢的任何日期,但您也可以将他们的选择限制在您选择的范围内。例如,此代码允许他们选择从今天开始的任何日期,但不能选择更早的日期:
MultiDatePicker("Select your preferred dates", selection: $dates, in: Date.now...)