如何在未选择日期时重置内联 iOS UIDatePicker
How to reset inline iOS UIDatePicker when no date is selected
使用新的内联 UIDatePicker 时(在 iOS 14 中引入)。如果没有选择日期,如何将日历重置为当前日期。
当运行出现以下情况时,当您选择了另一个日期时,显示的日历将重置为当前日期:
self.datePicker.setDate(Date(), animated: true)
但是,如果您创建一个新的内联 UIDatePicker 并开始滚动月份,运行同一行代码将不会更新视图,直到用户选择特定日期。
真奇怪...
我的猜测是,当使用较旧的“滚轮”日期选择器样式时,当滚轮停止滚动时会选择一个新日期...无法从所选日期“滚开”。
我的第二个猜测是,这个问题可能会在未来的 iOS 更新中得到改变(“修复”)。
无论如何,这里有一个解决方法...
- 获取今天的日期
- 从选择器中获取选定的日期
- 如果它们相同,用户可能滚动了月份/年份,所以
- 在“今天”上加一天
- 将选择器设置为“明天”(动画)
- 进行异步调用以将选取器设置为“今天”
- 如果日期不同,只需将选择器设置为“今天”(动画)
我只对此进行了快速测试,因此您需要彻底测试它。并且可能添加一些额外的检查——例如如果日历在显示选择器时滚动到午夜会发生什么;如果“今天”是这个月的最后一天,它看起来如何?等等
// get "today" date
let today = Date()
// get selected date
let pickerDate = self.datePicker.date
// are the dates the same day?
let todayIsSelected = Calendar.current.isDate(today, inSameDayAs:pickerDate)
if todayIsSelected {
// picker has today selected, but may have scrolled months...
// should never fail, but this unwraps the optional
guard let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: today) else {
return
}
// animate to "tomorrow"
self.datePicker.setDate(nextDay, animated: true)
// async call to animate to "today"
DispatchQueue.main.async {
self.datePicker.setDate(today, animated: true)
}
} else {
// picker has a different date selected
// so just animate to "today"
self.datePicker.setDate(today, animated: true)
}
编辑 - 完整示例:
class ScratchVC: UIViewController {
let datePicker = UIDatePicker()
let btn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
// Fallback on earlier versions
}
btn.backgroundColor = .red
btn.setTitle("Go To Today", for: [])
btn.setTitleColor(.white, for: .normal)
btn.setTitleColor(.gray, for: .highlighted)
btn.translatesAutoresizingMaskIntoConstraints = false
datePicker.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(datePicker)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
datePicker.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.9),
datePicker.centerXAnchor.constraint(equalTo: g.centerXAnchor),
datePicker.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
}
@objc func didTap(_ sender: Any) {
// get "today" date
let today = Date()
// get selected date
let pickerDate = self.datePicker.date
// are the dates the same day?
let todayIsSelected = Calendar.current.isDate(today, inSameDayAs:pickerDate)
if todayIsSelected {
// picker has today selected, but may have scrolled months...
// should never fail, but this unwraps the optional
guard let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: today) else {
return
}
// animate to "tomorrow"
self.datePicker.setDate(nextDay, animated: true)
// async call to animate to "today" - delay for 0.1 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
self.datePicker.setDate(today, animated: true)
})
} else {
// picker has a different date selected
// so just animate to "today"
self.datePicker.setDate(today, animated: true)
}
}
}
使用新的内联 UIDatePicker 时(在 iOS 14 中引入)。如果没有选择日期,如何将日历重置为当前日期。
当运行出现以下情况时,当您选择了另一个日期时,显示的日历将重置为当前日期:
self.datePicker.setDate(Date(), animated: true)
但是,如果您创建一个新的内联 UIDatePicker 并开始滚动月份,运行同一行代码将不会更新视图,直到用户选择特定日期。
真奇怪...
我的猜测是,当使用较旧的“滚轮”日期选择器样式时,当滚轮停止滚动时会选择一个新日期...无法从所选日期“滚开”。
我的第二个猜测是,这个问题可能会在未来的 iOS 更新中得到改变(“修复”)。
无论如何,这里有一个解决方法...
- 获取今天的日期
- 从选择器中获取选定的日期
- 如果它们相同,用户可能滚动了月份/年份,所以
- 在“今天”上加一天
- 将选择器设置为“明天”(动画)
- 进行异步调用以将选取器设置为“今天”
- 如果日期不同,只需将选择器设置为“今天”(动画)
我只对此进行了快速测试,因此您需要彻底测试它。并且可能添加一些额外的检查——例如如果日历在显示选择器时滚动到午夜会发生什么;如果“今天”是这个月的最后一天,它看起来如何?等等
// get "today" date
let today = Date()
// get selected date
let pickerDate = self.datePicker.date
// are the dates the same day?
let todayIsSelected = Calendar.current.isDate(today, inSameDayAs:pickerDate)
if todayIsSelected {
// picker has today selected, but may have scrolled months...
// should never fail, but this unwraps the optional
guard let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: today) else {
return
}
// animate to "tomorrow"
self.datePicker.setDate(nextDay, animated: true)
// async call to animate to "today"
DispatchQueue.main.async {
self.datePicker.setDate(today, animated: true)
}
} else {
// picker has a different date selected
// so just animate to "today"
self.datePicker.setDate(today, animated: true)
}
编辑 - 完整示例:
class ScratchVC: UIViewController {
let datePicker = UIDatePicker()
let btn = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else {
// Fallback on earlier versions
}
btn.backgroundColor = .red
btn.setTitle("Go To Today", for: [])
btn.setTitleColor(.white, for: .normal)
btn.setTitleColor(.gray, for: .highlighted)
btn.translatesAutoresizingMaskIntoConstraints = false
datePicker.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(btn)
view.addSubview(datePicker)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
btn.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
btn.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
btn.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),
datePicker.widthAnchor.constraint(equalTo: g.widthAnchor, multiplier: 0.9),
datePicker.centerXAnchor.constraint(equalTo: g.centerXAnchor),
datePicker.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
btn.addTarget(self, action: #selector(didTap(_:)), for: .touchUpInside)
}
@objc func didTap(_ sender: Any) {
// get "today" date
let today = Date()
// get selected date
let pickerDate = self.datePicker.date
// are the dates the same day?
let todayIsSelected = Calendar.current.isDate(today, inSameDayAs:pickerDate)
if todayIsSelected {
// picker has today selected, but may have scrolled months...
// should never fail, but this unwraps the optional
guard let nextDay = Calendar.current.date(byAdding: .day, value: 1, to: today) else {
return
}
// animate to "tomorrow"
self.datePicker.setDate(nextDay, animated: true)
// async call to animate to "today" - delay for 0.1 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
self.datePicker.setDate(today, animated: true)
})
} else {
// picker has a different date selected
// so just animate to "today"
self.datePicker.setDate(today, animated: true)
}
}
}