Is there a way to have a UIDatePicker show year only?
It is not possible to set only Year and Month. Only these modes are available as of iOS 13.5.1
time: A mode that displays the date in hours, minutes, and (optionally) an AM/PM designation. The exact items shown and their order depend upon the locale set. An example of this mode is [ 6 | 53 | PM ].
date: A mode that displays the date in months, days of the month, and years. The exact order of these items depends on the locale setting. An example of this mode is [ November | 15 | 2007 ].
dateAndTime: A mode that displays the date as unified day of the week, month, and day of the month values, plus hours, minutes, and (optionally) an AM/PM designation. The exact order and format of these items depends on the locale set. An example of this mode is [ Wed Nov 15 | 6 | 53 | PM ].
countDownTimer: A mode that displays hour and minute values, for example [ 1 | 53 ]. The application must set a timer to fire at the proper interval and set the date picker as the seconds tick down. UIDatePicker.Mode - developer.apple.com
You may build it using a custom UIPickerView.
class DatePickerViewController: UIViewController {
var dates = [Date]()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
var date = Date().addYear(n: -10)
let endDate = Date().addYear(n: 10)
repeat {
date = date.addMonth(n: 1)
dates.append(date)
} while date < endDate
let picker = UIPickerView()
picker.dataSource = self
picker.delegate = self
picker.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(picker)
picker.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
picker.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
picker.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
picker.heightAnchor.constraint(equalToConstant: 500).isActive = true
}
}
extension DatePickerViewController: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.dates.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
let date = self.dates[row]
return date.stringDate()
}
func view(forRow row: Int, forComponent component: Int) -> UIView? {
let label = UILabel()
return label
}
}
extension DatePickerViewController: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
let date = self.dates[row]
// Date that the user select.
print( date, date.stringDate())
}
}
extension Date {
public func addYear(n: Int) -> Date {
let calendar = Calendar.current
return calendar.date(byAdding: .year, value: n, to: self)!
}
public func addMonth(n: Int) -> Date {
let calendar = Calendar.current
return calendar.date(byAdding: .month, value: n, to: self)!
}
public func stringDate() -> String {
let calendar = Calendar.current
let dateFormatter = DateFormatter()
dateFormatter.timeZone = calendar.timeZone
dateFormatter.locale = calendar.locale
// Use YYYY to show year only.
// Use MMMM to show month only.
dateFormatter.setLocalizedDateFormatFromTemplate("MMMM YYYY")
let dateString = dateFormatter.string(from: self)
return dateString
}
}
Just change the following two lines- add/subtract as many years you wish to show.
var date = Date().addYear(n: -10)
let endDate = Date().addYear(n: 10)
I changed Sharukh Mastan's code slightly to always show the current year on top.
var formattedDate: String? = ""
let format = DateFormatter()
format.dateFormat = "yyyy"
formattedDate = format.string(from: date)
var yearsTillNow: [String] {
var years = [String]()
for i in (Int(formattedDate!)!-70..<Int(formattedDate!)!+1).reversed() {
years.append("\(i)")
}
return years
}
print(yearsTillNow)
This prints an array of years going back for 70 years which you can use as UIPickerView datasource