Jay
Jay

Reputation: 238

UIDatePicker to show year only

Is there a way to have a UIDatePicker show year only?

Upvotes: 16

Views: 28382

Answers (6)

mahan
mahan

Reputation: 14935

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)

Upvotes: 3

Robin
Robin

Reputation: 943

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

Upvotes: 0

Sharukh Mastan
Sharukh Mastan

Reputation: 1591

Just updating the existing answer in this post with Swift 3:

var yearsTillNow : [String] {
    var years = [String]()
    for i in (1970..<2018).reversed() {
        years.append("\(i)")
    }
    return years
}

Just use this for your UIPickerView datasource.

Upvotes: 3

The iOSDev
The iOSDev

Reputation: 5267

I have a little code that I was about to delete, but the snippet is better off if it is online somewhere. It's not amazing, but it is searchable!

Objective-C code to create an array of all years since 1960. Perfect for input into a UIPicker

//Get Current Year into i2
NSDateFormatter* formatter = [[[NSDateFormatter alloc] init]autorelease];
[formatter setDateFormat:@"yyyy"];
int i2  = [[formatter stringFromDate:[NSDate date]] intValue];


//Create Years Array from 1960 to This year
years = [[NSMutableArray alloc] init];
for (int i=1960; i<=i2; i++) {
    [years addObject:[NSString stringWithFormat:@"%d",i]];
}

The UIPickerView data source and delegate methods:

- (NSInteger)numberOfComponentsInPickerView: (UIPickerView*)thePickerView {
    return 1;
}

- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component
{
    return [years count];
}
- (NSString *)pickerView:(UIPickerView *)thePickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
    return [years objectAtIndex:row];
}

Don't forget the declaration in the interface

//Data
NSMutableArray *years;

The out put of it is

enter image description here

Referenced from here

Upvotes: 15

cowbear16
cowbear16

Reputation: 55

I cannot comment, but I was playing around with the answer by Wolvorin and I decided to do a way where the most recent year would be at the top of the Picker. Currently, his code goes oldest to most recent years.

All I changes was in my NSMutableArray setup in the viewDidLoad instead of his:

//Create Years Array from 1960 to This year
years = [[NSMutableArray alloc] init];
for (int i=1960; i<=i2; i++) {
    [years addObject:[NSString stringWithFormat:@"%d",i]];
}

I used:

years = [[NSMutableArray alloc] init];
for (int i1=i2; i1<=i2 & i1>=1920; i1--) {
    [years addObject:[NSString stringWithFormat:@"%d",i1]];
}

Which does the for in reversed order going from the most recent date, then subtracting one year (and adding it to the Array) until it gets to 1920.

My formula:

int i1=i2; i1<=i2 & i1>=1920; i1--

Upvotes: 3

James Zaghini
James Zaghini

Reputation: 4001

You can't do this with UIDatePicker, but you can with UIPicker.

You need to create an array for the years, and add them to the UIPicker using the UIPicker delegates

Here's a tutorial.

Upvotes: 5

Related Questions