UIDatePicker 选择月份和年份

Posted

技术标签:

【中文标题】UIDatePicker 选择月份和年份【英文标题】:UIDatePicker select Month and Year 【发布时间】:2011-03-21 20:47:48 【问题描述】:

我需要UIDatePicker 来仅选择月份和年份。我检查了类参考文件。看起来UIDatePickerUIView。我想象UIPickerView 可能是一个子视图,如果我能抓住它,我可以隐藏它。但不是。那是不可能的。那我必须创建自己的自定义选择器吗?有什么想法吗?

【问题讨论】:

自从提出这个问题后,我推荐的SRMonthPicker等开源库已经添加到CocoaPods中了。 【参考方案1】:

这是获得相同效果的解决方案。要使用此 sn-p 代码,您应该将“Indentity 检查器”的“自定义类”中的 nib 文件中的 UIPickerView 替换为 CDatePickerViewEx

.h 文件

#import <UIKit/UIKit.h>

@interface CDatePickerViewEx : UIPickerView <UIPickerViewDelegate, UIPickerViewDataSource> 

@property (nonatomic, strong, readonly) NSDate *date;
-(void)selectToday;

@end

.m 文件

#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 CDatePickerViewEx

const 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

【讨论】:

你能解释一下,如何使用这个类。 这里是 githab 项目的链接:github.com/IgorFedorchuk/MonthYearDatePicker 您能告诉我使用 bigRowMonthCount 和 bigRowYearCount 等常量的目的吗?【参考方案2】:

是的,您可能只想制作自己的选择器。但是,您不必将其子类化或其他任何东西;只需使用通用 UIPickerView 并从您的 UIPickerViewDelegate/UIPickerViewDataSource 方法返回适当的值。

【讨论】:

您如何处理必须阻止选择器选择未来日期的事实?【参考方案3】:

我用 Swift 重写了 Igor 的答案:

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)
    


【讨论】:

【参考方案4】:

,这不能使用股票UIDatePicker来完成。

UIDatePickerModeDate

日期选择器显示月、日 月份和年份。这些的确切顺序 项目取决于区域设置。 这种模式的一个例子是 [11 月 | 15 | 2007]。

来源:UIDatePicker class reference

我建议自定义 UIPickerView 以使用两个组件,并使用从 NSDateFormatter 检索到的月份和年份符号填充其行。

【讨论】:

用户在此 PickerView 中选择日期后,如何获取其值? @Brave - pickerView:didSelectRow:inComponent:【参考方案5】:

在 Swift 4 中重写 answer 的 RossP:

import Foundation

@objc class ShortDatePickerView: 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: Date 
        get 
            let month = self.months[selectedRow(inComponent: Component.Month.rawValue) % months.count]
            let year = self.years[selectedRow(inComponent: Component.Year.rawValue) % years.count]
            let formatter = DateFormatter()
            formatter.dateFormat = "MM yyyy"
            return formatter.date(from: "\(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 = Calendar.current.dateComponents([.year], from: Date()).year
        maxYear = minYear! + 10

        self.delegate = self
        self.dataSource = self

        monthSelectedTextColor = .blue
        monthTextColor = .black

        yearSelectedTextColor = .blue
        yearTextColor = .black

        monthSelectedFont = .boldSystemFont(ofSize: 17)
        monthFont = .boldSystemFont(ofSize: 17)

        yearSelectedFont = .boldSystemFont(ofSize: 17)
        yearFont = .boldSystemFont(ofSize: 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.index(of: cellMonth)!)
                    row = row + Double(bigRowMonthsCount / 2)
                    break
                
            

            for cellYear in years 
                if cellYear == currentYearName 
                    section = Double(years.index(of: cellYear)!)
                    section = section + Double(bigRowYearsCount / 2)
                    break
                
            

            return NSIndexPath(row: Int(row), section: Int(section))
        
    

    var currentMonthName: String 
        get 
            let formatter = DateFormatter()
            let locale = Locale.init(identifier: "en_US")
            formatter.locale = locale
            formatter.dateFormat = "MM"
            return formatter.string(from: Date())
        
    

    var currentYearName: String 
        get 
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy"
            return formatter.string(from: Date())
        
    


    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 = .center
        label.backgroundColor = .clear
        label.isUserInteractionEnabled = false
        label.tag = LABEL_TAG
        return label
    


extension ShortDatePickerView: 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, reusing 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: component)
        

        returnView.font = selected ? selectedFontForComponent(component: component) : fontForComponent(component: component)
        returnView.textColor = selected ? selectedColorForComponent(component: component) : colorForComponent(component: component)

        returnView.text = titleForRow(row: row, forComponent: component)

        return returnView
    

    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat 
        return CGFloat(rowHeight)
    

    func numberOfComponents(in pickerView: UIPickerView) -> Int 
        return 2
    

【讨论】:

【参考方案6】:

我有一个懒惰的解决方法——我制作了一个 10x10 黑色的 png。 然后,我将其用作带有 scaletofill 图像视图的遮罩,其大小可以完美地覆盖日期列。我将 alpha 设置为 0.75 以勉强显示该列。对用户来说很清楚,看起来还不错。

【讨论】:

谨防本地化。日期列并不总是第二个;在法国,我们将日期格式化为:dd/MM/yyyy,所以选择器会读取类似 [ 16 |费夫里尔 | 2012] 好点,如果支持本地化,您必须相应地移动它,而我们不支持。到那时,您不妨像 Noah 所说的那样重写选择器方法,但是用户并不清楚发生了什么,所以您不妨制作自己的自定义选择器!【参考方案7】:

当值更改事件发生时,我只是将日期重置为每月的第一天,如下所示。所以当用户选择一天时,它会滚动回第一天。我使用以下选择开始日期(月和年)。

-(void) resetDay 

 NSDate *currentDate  = [startDatePicker date];
 NSCalendar *gregorian = [[NSCalendar alloc]  initWithCalendarIdentifier:NSGregorianCalendar];
 NSDateComponents *componentsYear = [gregorian components:(NSYearCalendarUnit| NSMonthCalendarUnit) fromDate:currentDate];
 NSInteger yearNum = [ componentsYear year];
 NSInteger monthNum = [componentsYear month];
 NSLog(@"Month %d, Year %d", monthNum, yearNum);

 NSDateComponents *componentStartDate = [[NSDateComponents alloc] init];
 [componentStartDate setDay:1];
 [componentStartDate setMonth:monthNum];
 [componentStartDate setYear:yearNum];
 NSDate *startDate = [gregorian dateFromComponents:componentStartDate];

 [startDatePicker setDate:startDate animated:YES];
 [componentStartDate release];
 [gregorian release];

对于选择结束日期(月和年),它有点长,因为我想将日期设置为所选月份的 31 日或 30 日。

-(NSInteger) findDaysInYear 
    NSDate *currentDate = [NSDate date];
    NSCalendar *gregorian = [[NSCalendar alloc]  initWithCalendarIdentifier:NSGregorianCalendar];


    NSDateComponents *componentsYear = [gregorian components:(NSYearCalendarUnit)   fromDate:currentDate];
    NSInteger yearNum = [ componentsYear year];

    NSDateComponents *componentStartDate = [[NSDateComponents alloc] init];
    [componentStartDate setDay:1];
    [componentStartDate setMonth:1];
    [componentStartDate setYear:yearNum];
    NSDate *startDate = [gregorian dateFromComponents:componentStartDate];
    NSLog(@"Start date set to %@", startDate);

    NSDateComponents *componentEndDate = [[NSDateComponents alloc] init];
    [componentEndDate setDay:31];
    [componentEndDate setMonth:12];
    [componentEndDate setYear:yearNum];

    NSDate *endDate = [gregorian dateFromComponents:componentEndDate];
    NSLog(@"End date set to %@", endDate);

    NSUInteger unitFlags = NSDayCalendarUnit ;
    NSDateComponents *componentsDay = [gregorian components:unitFlags   fromDate:startDate    toDate:endDate options:0];
    NSInteger days = [componentsDay day] +1;
    NSLog(@"Number of days in the year:%d, is %d", yearNum,days);
    [componentEndDate release];
    [componentStartDate release];
    [gregorian release];
    if (days != 365 && days != 366) 
        return 366;
    
    return days;

-(NSInteger) findDaysInMonth:(NSInteger) monthNum 

    NSInteger days = 30;


    switch (monthNum) 
        case 1:
            days = 31;
            break;
        case 2:
            if([self findDaysInYear] == 366)        days = 29;
            else days = 28;
            break;
        case 3:
            days = 31;
            break;
        case 4:
            days = 30;
            break;
        case 5:
            days = 31;
            break;
        case 6:
            days = 30;
            break;
        case 7:
            days = 31;
            break;
        case 8:
            days = 31;
            break;
        case 9:
            days = 30;
            break;
        case 10:
            days = 31;
            break;
        case 11:
            days = 30;
            break;
        case 12:
            days = 31;
            break;
        default:
            break;
    
    return days;



-(void) resetDay 

    NSDate *currentDate  = [endDatePicker date];
    NSCalendar *gregorian = [[NSCalendar alloc]  initWithCalendarIdentifier:NSGregorianCalendar];
    NSDateComponents *componentsYear = [gregorian components:(NSYearCalendarUnit| NSMonthCalendarUnit)  fromDate:currentDate];
    NSInteger yearNum = [ componentsYear year];
    NSInteger monthNum = [componentsYear month];

    NSDateComponents *componentStartDate = [[NSDateComponents alloc] init];
    [componentStartDate setDay:[self findDaysInMonth:monthNum]];
    [componentStartDate setMonth:monthNum];
    [componentStartDate setYear:yearNum];
    NSDate *startDate = [gregorian dateFromComponents:componentStartDate];

    [endDatePicker setDate:startDate animated:YES];
    [componentStartDate release];
    [gregorian release];

【讨论】:

【参考方案8】:

我正在使用一个简单的UIPickerView,其中包含 2 个组件(月和年)。

并在每行选择时调用UIPickerViewDelegate

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component

    //Today year and month
    NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
    [dateFormat setDateFormat:@"yyyy"];
    int year = [[dateFormat stringFromDate:[NSDate date]] intValue];
    [dateFormat setDateFormat:@"MM"];
    int month = [[dateFormat stringFromDate:[NSDate date]] intValue] - 1;

    //picker year and month
    int selectedYear = [[self.yearsArray objectAtIndex:[pickerView selectedRowInComponent:1]] intValue];
    int selectedMonth = [pickerView selectedRowInComponent:0];

    // if month in the past, change the month
    if (year == selectedYear && selectedMonth < month)
    
        [pickerView selectRow:month inComponent:0 animated:YES];
    

【讨论】:

也许将 NSDateFormatter 作为 ivar,而不是在每次选择一行时初始化一个新的。【参考方案9】:

您可以使用名为

的开源库

AKMonthYearPickerView

import AKMonthYearPickerView

AKMonthYearPickerView.sharedInstance.show(vc: viewController, doneHandler: doneHandler, completetionalHandler: completetionalHandler)

https://github.com/ali-cs/AKMonthYearPickerView

使用 pod 安装它,

pod 'AKMonthYearPickerView' 

自定义往年限制和条形颜色

AKMonthYearPickerView.sharedInstance.barTintColor =   UIColor.blue

AKMonthYearPickerView.sharedInstance.previousYear = 4

【讨论】:

【参考方案10】:

您可以使用 UICustomDatePicker 仅显示月份和年份选项

https://github.com/Anandsan/UICustomDatePicker

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UICustomDatePicker *customDatePicker;

@end

-(void) viewDidLoad 

 [super viewDidLoad];

 self.customDatePicker.option = NSCustomDatePickerOptionLongMonth | NSCustomDatePickerOptionYear;

 self.customDatePicker.order = NSCustomDatePickerOrderMonthDayAndYear;



 -(IBAction)didCustomDatePickerValueChanged:(id)sender 

    NSLog(@"%@",[(UICustomDatePicker *)sender currentDate]);


Refer the Screen

【讨论】:

欢迎来到 SO :) 请不要回答 只是 一个链接。还可以添加解释和/或一些代码以提高答案的质量。【参考方案11】:

您可以像这样非常轻松地隐藏日期列:

datePicker.subviews[0].subviews[0].subviews[1].hidden = true

享受:)

【讨论】:

@CanAksoy 好的!它可能没有工作!但是你的问题到底是什么?我刚刚发现 UIDatePickerNSLocale 是什么很重要。第三组子视图是 [月][日][年] 列。但是,例如法语,它将是 [day][month][year],而我上面的解决方案将隐藏月份列并显示日期列。因此,例如,如果您想隐藏法语集 UIDatePicker 的日期列,您可以这样做datePicker.subviews[0].subviews[0].subviews[0].hidden = true。您还可以将月份列移动一点....subviews[1].frame.origin.x -= 50.0 虽然这可能非常有效,但强烈不推荐(因此,反对票)。你永远不应该像这样乱用 ios 标准控件,因为你不能保证它会在下一次 iOS 更新后保持在这个确切的层次结构索引中。而是(正如大多数其他答案所暗示的那样)继承默认的 UIPickerView 并给它一个仅满足一个月和一年的需求的委托。 抱歉,选民们讨厌我,因为这是他们无法想出的单线简单解决方案;)和平!

以上是关于UIDatePicker 选择月份和年份的主要内容,如果未能解决你的问题,请参考以下文章

在IOS中设置Datepicker只选择月份和年份

如何仅在月份、日期和年份中更改我的日期选择器显示

UIDatePicker 的自定义

iOS Swift 3 - UIDatePicker

Bootstrap 日期选择器显示月份和年份菜单

SQL Server 2008 仅在月份和年份之间选择数据