UIDatePicker select Month and Year
Here is a solution to get the same effect. For using this snippet of code you should replace UIPickerView
to CDatePickerViewEx
in nib file in "Custom class" of "Indentity inspector".
.h file
#import <UIKit/UIKit.h>@interface CDatePickerViewEx : UIPickerView <UIPickerViewDelegate, UIPickerViewDataSource> @property (nonatomic, strong, readonly) NSDate *date;-(void)selectToday;@end
.m file
#import "CDatePickerViewEx.h"// Identifiers of components#define MONTH ( 0 )#define YEAR ( 1 ) // Identifies for component views#define LABEL_TAG 43@interface CDatePickerViewEx()@property (nonatomic, strong) NSIndexPath *todayIndexPath;@property (nonatomic, strong) NSArray *months;@property (nonatomic, strong) NSArray *years;-(NSArray *)nameOfYears;-(NSArray *)nameOfMonths;-(CGFloat)componentWidth;-(UILabel *)labelForComponent:(NSInteger)component selected:(BOOL)selected;-(NSString *)titleForRow:(NSInteger)row forComponent:(NSInteger)component;-(NSIndexPath *)todayPath;-(NSInteger)bigRowMonthCount;-(NSInteger)bigRowYearCount;-(NSString *)currentMonthName;-(NSString *)currentYearName;@end@implementation CDatePickerViewExconst NSInteger bigRowCount = 1000; const NSInteger minYear = 2008;const NSInteger maxYear = 2030;const CGFloat rowHeight = 44.f;const NSInteger numberOfComponents = 2;@synthesize todayIndexPath;@synthesize months;@synthesize years = _years;-(void)awakeFromNib{ [super awakeFromNib]; self.months = [self nameOfMonths]; self.years = [self nameOfYears]; self.todayIndexPath = [self todayPath]; self.delegate = self; self.dataSource = self; [self selectToday];}-(NSDate *)date{ NSInteger monthCount = [self.months count]; NSString *month = [self.months objectAtIndex:([self selectedRowInComponent:MONTH] % monthCount)]; NSInteger yearCount = [self.years count]; NSString *year = [self.years objectAtIndex:([self selectedRowInComponent:YEAR] % yearCount)]; NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"MMMM:yyyy"]; NSDate *date = [formatter dateFromString:[NSString stringWithFormat:@"%@:%@", month, year]]; return date;}#pragma mark - UIPickerViewDelegate-(CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component{ return [self componentWidth];}-(UIView *)pickerView: (UIPickerView *)pickerView viewForRow: (NSInteger)row forComponent: (NSInteger)component reusingView: (UIView *)view{ BOOL selected = NO; if(component == MONTH) { NSInteger monthCount = [self.months count]; NSString *monthName = [self.months objectAtIndex:(row % monthCount)]; NSString *currentMonthName = [self currentMonthName]; if([monthName isEqualToString:currentMonthName] == YES) { selected = YES; } } else { NSInteger yearCount = [self.years count]; NSString *yearName = [self.years objectAtIndex:(row % yearCount)]; NSString *currenrYearName = [self currentYearName]; if([yearName isEqualToString:currenrYearName] == YES) { selected = YES; } } UILabel *returnView = nil; if(view.tag == LABEL_TAG) { returnView = (UILabel *)view; } else { returnView = [self labelForComponent: component selected: selected]; } returnView.textColor = selected ? [UIColor blueColor] : [UIColor blackColor]; returnView.text = [self titleForRow:row forComponent:component]; return returnView;}-(CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component{ return rowHeight;}#pragma mark - UIPickerViewDataSource- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{ return numberOfComponents;}-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{ if(component == MONTH) { return [self bigRowMonthCount]; } return [self bigRowYearCount];}#pragma mark - Util-(NSInteger)bigRowMonthCount{ return [self.months count] * bigRowCount;}-(NSInteger)bigRowYearCount{ return [self.years count] * bigRowCount;}-(CGFloat)componentWidth{ return self.bounds.size.width / numberOfComponents;}-(NSString *)titleForRow:(NSInteger)row forComponent:(NSInteger)component{ if(component == MONTH) { NSInteger monthCount = [self.months count]; return [self.months objectAtIndex:(row % monthCount)]; } NSInteger yearCount = [self.years count]; return [self.years objectAtIndex:(row % yearCount)];}-(UILabel *)labelForComponent:(NSInteger)component selected:(BOOL)selected{ CGRect frame = CGRectMake(0.f, 0.f, [self componentWidth],rowHeight); UILabel *label = [[UILabel alloc] initWithFrame:frame]; label.textAlignment = UITextAlignmentCenter; label.backgroundColor = [UIColor clearColor]; label.textColor = selected ? [UIColor blueColor] : [UIColor blackColor]; label.font = [UIFont boldSystemFontOfSize:18.f]; label.userInteractionEnabled = NO; label.tag = LABEL_TAG; return label;}-(NSArray *)nameOfMonths{ NSDateFormatter *dateFormatter = [NSDateFormatter new]; return [dateFormatter standaloneMonthSymbols];}-(NSArray *)nameOfYears{ NSMutableArray *years = [NSMutableArray array]; for(NSInteger year = minYear; year <= maxYear; year++) { NSString *yearStr = [NSString stringWithFormat:@"%i", year]; [years addObject:yearStr]; } return years;}-(void)selectToday{ [self selectRow: self.todayIndexPath.row inComponent: MONTH animated: NO]; [self selectRow: self.todayIndexPath.section inComponent: YEAR animated: NO];}-(NSIndexPath *)todayPath // row - month ; section - year{ CGFloat row = 0.f; CGFloat section = 0.f; NSString *month = [self currentMonthName]; NSString *year = [self currentYearName]; //set table on the middle for(NSString *cellMonth in self.months) { if([cellMonth isEqualToString:month]) { row = [self.months indexOfObject:cellMonth]; row = row + [self bigRowMonthCount] / 2; break; } } for(NSString *cellYear in self.years) { if([cellYear isEqualToString:year]) { section = [self.years indexOfObject:cellYear]; section = section + [self bigRowYearCount] / 2; break; } } return [NSIndexPath indexPathForRow:row inSection:section];}-(NSString *)currentMonthName{ NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"MMMM"]; return [formatter stringFromDate:[NSDate date]];}-(NSString *)currentYearName{ NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"yyyy"]; return [formatter stringFromDate:[NSDate date]];}@end
Yeah, you probably want to just make your own picker. You don't have to subclass it or anything, though; just use a generic UIPickerView
and return appropriate values from your UIPickerViewDelegate
/UIPickerViewDataSource
methods.
I rewrote Igor's answer in Swift:
class CLIVEDatePickerView: UIPickerView { enum Component: Int { case Month = 0 case Year = 1 } let LABEL_TAG = 43 let bigRowCount = 1000 let numberOfComponentsRequired = 2 let months = ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"] var years: [String] { get { var years: [String] = [String]() for i in minYear...maxYear { years.append("\(i)") } return years; } } var bigRowMonthsCount: Int { get { return bigRowCount * months.count } } var bigRowYearsCount: Int { get { return bigRowCount * years.count } } var monthSelectedTextColor: UIColor? var monthTextColor: UIColor? var yearSelectedTextColor: UIColor? var yearTextColor: UIColor? var monthSelectedFont: UIFont? var monthFont: UIFont? var yearSelectedFont: UIFont? var yearFont: UIFont? let rowHeight: NSInteger = 44 /** Will be returned in user's current TimeZone settings **/ var date: NSDate { get { let month = self.months[selectedRowInComponent(Component.Month.rawValue) % months.count] let year = self.years[selectedRowInComponent(Component.Year.rawValue) % years.count] let formatter = NSDateFormatter() formatter.dateFormat = "MM yyyy" return formatter.dateFromString("\(month) \(year)")! } } var minYear: Int! var maxYear: Int! override init(frame: CGRect) { super.init(frame: frame) loadDefaultParameters() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) loadDefaultParameters() } override func awakeFromNib() { super.awakeFromNib() loadDefaultParameters() } func loadDefaultParameters() { minYear = NSCalendar.currentCalendar().components(NSCalendarUnit.Year, fromDate: NSDate()).year maxYear = minYear! + 10 self.delegate = self self.dataSource = self monthSelectedTextColor = UIColor.blueColor() monthTextColor = UIColor.blackColor() yearSelectedTextColor = UIColor.blueColor() yearTextColor = UIColor.blackColor() monthSelectedFont = UIFont.boldSystemFontOfSize(17) monthFont = UIFont.boldSystemFontOfSize(17) yearSelectedFont = UIFont.boldSystemFontOfSize(17) yearFont = UIFont.boldSystemFontOfSize(17) } func setup(minYear: NSInteger, andMaxYear maxYear: NSInteger) { self.minYear = minYear if maxYear > minYear { self.maxYear = maxYear } else { self.maxYear = minYear + 10 } } func selectToday() { selectRow(todayIndexPath.row, inComponent: Component.Month.rawValue, animated: false) selectRow(todayIndexPath.section, inComponent: Component.Year.rawValue, animated: false) } var todayIndexPath: NSIndexPath { get { var row = 0.0 var section = 0.0 for cellMonth in months { if cellMonth == currentMonthName { row = Double(months.indexOf(cellMonth)!) row = row + Double(bigRowMonthsCount / 2) break } } for cellYear in years { if cellYear == currentYearName { section = Double(years.indexOf(cellYear)!) section = section + Double(bigRowYearsCount / 2) break } } return NSIndexPath(forRow: Int(row), inSection: Int(section)) } } var currentMonthName: String { get { let formatter = NSDateFormatter() let locale = NSLocale(localeIdentifier: "en_US") formatter.locale = locale formatter.dateFormat = "MM" return formatter.stringFromDate(NSDate()) } } var currentYearName: String { get { let formatter = NSDateFormatter() formatter.dateFormat = "yyyy" return formatter.stringFromDate(NSDate()) } } func selectedColorForComponent(component: NSInteger) -> UIColor { if component == Component.Month.rawValue { return monthSelectedTextColor! } return yearSelectedTextColor! } func colorForComponent(component: NSInteger) -> UIColor { if component == Component.Month.rawValue { return monthTextColor! } return yearTextColor! } func selectedFontForComponent(component: NSInteger) -> UIFont { if component == Component.Month.rawValue { return monthSelectedFont! } return yearSelectedFont! } func fontForComponent(component: NSInteger) -> UIFont { if component == Component.Month.rawValue { return monthFont! } return yearFont! } func titleForRow(row: Int, forComponent component: Int) -> String? { if component == Component.Month.rawValue { return self.months[row % self.months.count] } return self.years[row % self.years.count] } func labelForComponent(component: NSInteger) -> UILabel { let frame = CGRect(x: 0.0, y: 0.0, width: bounds.size.width, height: CGFloat(rowHeight)) let label = UILabel(frame: frame) label.textAlignment = NSTextAlignment.Center label.backgroundColor = UIColor.clearColor() label.userInteractionEnabled = false label.tag = LABEL_TAG return label }}extension CLIVEDatePickerView: UIPickerViewDelegate, UIPickerViewDataSource { func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { return numberOfComponentsRequired } func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { if(component == Component.Month.rawValue) { return bigRowMonthsCount } else { return bigRowYearsCount } } func pickerView(pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat { return self.bounds.size.width / CGFloat(numberOfComponentsRequired) } func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView?) -> UIView { var selected = false if component == Component.Month.rawValue { let monthName = self.months[(row % self.months.count)] if monthName == currentMonthName { selected = true } } else { let yearName = self.years[(row % self.years.count)] if yearName == currentYearName { selected = true } } var returnView: UILabel if view?.tag == LABEL_TAG { returnView = view as! UILabel } else { returnView = labelForComponent(component) } returnView.font = selected ? selectedFontForComponent(component) : fontForComponent(component) returnView.textColor = selected ? selectedColorForComponent(component) : colorForComponent(component) returnView.text = titleForRow(row, forComponent: component) return returnView } func pickerView(pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { return CGFloat(rowHeight) }}