iOS 仿支付宝的时间选择器UIPickerView

前言
仿支付宝的日期选择器。在支付宝账单里面有根据日期进行筛选功能,项目中正好要用到,参考BRDatePickerView的数据源和Date分类。以此做下记录

1.效果图如下:

屏幕快照 2019-07-12 下午5.02.01.png

屏幕快照 2019-07-12 下午5.02.10.png

2.实现

.h代码

//  日/月 选择

#import "UIBaseViewController.h"

NS_ASSUME_NONNULL_BEGIN
typedef void(^monthDayBlock)(NSInteger dateType,NSString *startTime,NSString *endTime);
@interface ChooseDateViewController : UIBaseViewController
@property (copy,nonatomic)monthDayBlock block;
@end

NS_ASSUME_NONNULL_END

.m代码

#import "ChooseDateViewController.h"
#import "YearDayModel.h"

/// 弹出日期类型
typedef NS_ENUM(NSInteger, MLDatePickerMode) {
    // 年月
    MLDatePickerModeYM = 0,         // yyyy-MM
    // 年月日
    MLDatePickerModeYMD = 1,        // yyyy-MM-dd
};

@interface ChooseDateViewController ()<UITextFieldDelegate,UIPickerViewDelegate,UIPickerViewDataSource>{
    // 记录 年、月、日当前选择的位置
    NSInteger _yearIndex;
    NSInteger _monthIndex;
    NSInteger _dayIndex;
}

@property (weak, nonatomic) IBOutlet UIButton *chooseBtn;//完成按钮
@property (weak, nonatomic) IBOutlet UIView *dayView; //年月日view
@property (weak, nonatomic) IBOutlet UIView *monthView;//年月view

@property (weak, nonatomic) IBOutlet UITextField *startDayTF;//按日选择 开始
@property (weak, nonatomic) IBOutlet UITextField *endDayTF;//按日选择 结束
@property (weak, nonatomic) IBOutlet UITextField *monthTF;//按月选择

@property (weak, nonatomic) IBOutlet UIPickerView *YMpickView;//年月
@property (assign,nonatomic)NSInteger tag;//记录年月日的开始时间和结束时间TF


/// 日期存储数组
@property(nonatomic, strong) NSArray *yearArr;
@property(nonatomic, strong) NSArray *monthArr;
@property(nonatomic, strong) NSArray *dayArr;
/** 显示类型 */
@property (nonatomic, assign) MLDatePickerMode showType;
/** 限制最小日期 */
@property (nonatomic, strong) NSDate *minLimitDate;
/** 限制最大日期 */
@property (nonatomic, strong) NSDate *maxLimitDate;
/** 当前选择的日期 */
@property (nonatomic, strong) NSDate *selectDate;
/** 选择的日期的格式 */
@property (nonatomic, strong) NSString *selectDateFormatter;

@end

@implementation ChooseDateViewController


- (void)viewDidLoad{
    [super viewDidLoad];
    
    self.navBar.titleLabel.text = @"选择时间";
    self.view.backgroundColor = [UIColor whiteColor];
    
    self.YMpickView.showsSelectionIndicator = YES;
    //设置UIPickerView的代理
    self.YMpickView.delegate =self;
    self.YMpickView.dataSource =self;
    // 设置子视图的大小随着父视图变化
    self.YMpickView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;

    
    self.monthTF.delegate = self;
    self.endDayTF.delegate = self;
    self.startDayTF.delegate = self;
    
    self.tag = self.monthTF.tag = 100;
    self.startDayTF.tag = 101;
    self.endDayTF.tag = 102;
    
}

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    //界面出现时再获取数据源否则不自动滚动
    //默认选择月
    self.showType = MLDatePickerModeYM;
    [self setUP];
}

#pragma mark - setUP

- (void)setUP{
    self.dayView.hidden = (self.showType == MLDatePickerModeYM);
    self.monthView.hidden = !(self.showType == MLDatePickerModeYM);
    [self setupSelectDateFormatter:self.showType];
    [self setupMinMaxValue:self.showType];
    // 不设置默认日期,就默认选中今天的日期
    NSString *defaultSelValue = [NSDate br_getDateString:[NSDate date] format:self.selectDateFormatter];
    self.selectDate = [NSDate br_getDate:defaultSelValue format:self.selectDateFormatter];
    [self initDefaultDateArray];
    [self scrollToSelectDate:self.selectDate animated:NO];
}

- (void)setupSelectDateFormatter:(MLDatePickerMode)model {
    switch (model) {
        case MLDatePickerModeYM:
        {
            self.selectDateFormatter = @"yyyy-MM";
        }
            break;
        case MLDatePickerModeYMD:
        {
            self.selectDateFormatter = @"yyyy-MM-dd";
        }
            break;
            
        default:
            break;
    }
}

- (void)setupMinMaxValue:(MLDatePickerMode)model {
    switch (model) {
        case MLDatePickerModeYM:
        {
            self.minLimitDate = [NSDate br_setYear:2015 month:1];
            NSDate *now = [NSDate date];
            self.maxLimitDate = [NSDate br_setYear:now.br_year month:now.br_month];
        }
            break;
            
        case MLDatePickerModeYMD:
        {
            self.minLimitDate = [NSDate br_setYear:2015 month:1 day:1];
            NSDate *now = [NSDate date];
            self.maxLimitDate = [NSDate br_setYear:now.br_year  month:now.br_month day:now.br_day];
        }
            break;
            
        default:
            break;
    }
}

#pragma mark - 设置日期数据源数组
- (void)initDefaultDateArray {
    // 1. 设置 yearArr 数组
    [self setupYearArr];
    // 2.设置 monthArr 数组
    [self setupMonthArr:self.selectDate.br_year];
    // 3.设置 dayArr 数组
    [self setupDayArr:self.selectDate.br_year month:self.selectDate.br_month];
    
    //刷新界面
    [self.YMpickView reloadAllComponents];
    
    // 根据 默认选择的日期 计算出 对应的索引
    _yearIndex = self.selectDate.br_year - self.minLimitDate.br_year;
    _monthIndex = self.selectDate.br_month - ((_yearIndex == 0) ? self.minLimitDate.br_month : 1);
    _dayIndex = self.selectDate.br_day - ((_yearIndex == 0 && _monthIndex == 0) ? self.minLimitDate.br_day : 1);
}

#pragma mark - 更新日期数据源数组
- (void)updateDateArray {
    NSInteger year = [self.yearArr[_yearIndex] integerValue];
    // 1.设置 monthArr 数组
    [self setupMonthArr:year];
    // 更新索引:防止更新 monthArr 后数组越界
    _monthIndex = (_monthIndex > self.monthArr.count - 1) ? (self.monthArr.count - 1) : _monthIndex;
    
    NSInteger month = [self.monthArr[_monthIndex] integerValue];
    // 2.设置 dayArr 数组
    [self setupDayArr:year month:month];
    // 更新索引:防止更新 dayArr 后数组越界
    _dayIndex = (_dayIndex > self.dayArr.count - 1) ? (self.dayArr.count - 1) : _dayIndex;
}

// 设置 yearArr 数组
- (void)setupYearArr {
    NSMutableArray *tempArr = [NSMutableArray array];
    for (NSInteger i = self.minLimitDate.br_year; i <= self.maxLimitDate.br_year; i++) {
        [tempArr addObject:[@(i) stringValue]];
    }
    self.yearArr = [tempArr copy];
}

// 设置 monthArr 数组
- (void)setupMonthArr:(NSInteger)year {
    NSInteger startMonth = 1;
    NSInteger endMonth = 12;
    if (year == self.minLimitDate.br_year) {
        startMonth = self.minLimitDate.br_month;
    }
    if (year == self.maxLimitDate.br_year) {
        endMonth = self.maxLimitDate.br_month;
    }
    NSMutableArray *tempArr = [NSMutableArray arrayWithCapacity:(endMonth - startMonth + 1)];
    for (NSInteger i = startMonth; i <= endMonth; i++) {
        [tempArr addObject:[@(i) stringValue]];
    }
    self.monthArr = [tempArr copy];
}

// 设置 dayArr 数组
- (void)setupDayArr:(NSInteger)year month:(NSInteger)month {
    NSInteger startDay = 1;
    NSInteger endDay = [NSDate br_getDaysInYear:year month:month];
    if (year == self.minLimitDate.br_year && month == self.minLimitDate.br_month) {
        startDay = self.minLimitDate.br_day;
    }
    if (year == self.maxLimitDate.br_year && month == self.maxLimitDate.br_month) {
        endDay = self.maxLimitDate.br_day;
    }
    NSMutableArray *tempArr = [NSMutableArray array];
    for (NSInteger i = startDay; i <= endDay; i++) {
        [tempArr addObject:[NSString stringWithFormat:@"%zi",i]];
    }
    self.dayArr = [tempArr copy];
}


#pragma mark - 滚动到指定的时间位置
- (void)scrollToSelectDate:(NSDate *)selectDate animated:(BOOL)animated {
    // 根据 当前选择的日期 计算出 对应的索引
    NSInteger yearIndex = selectDate.br_year - self.minLimitDate.br_year;
    NSInteger monthIndex = selectDate.br_month - ((yearIndex == 0) ? self.minLimitDate.br_month : 1);
    NSInteger dayIndex = selectDate.br_day - ((yearIndex == 0 && monthIndex == 0) ? self.minLimitDate.br_day : 1);
    
    NSArray *indexArr = [NSArray array];
    if (self.showType == MLDatePickerModeYMD) {
        indexArr = @[@(yearIndex), @(monthIndex), @(dayIndex)];
    } else if (self.showType == MLDatePickerModeYM) {
        indexArr = @[@(yearIndex), @(monthIndex)];
    }
    for (NSInteger i = 0; i < indexArr.count; i++) {
        [self.YMpickView selectRow:[indexArr[i] integerValue] inComponent:i animated:animated];
    }
}

#pragma mark - UIPickerViewDataSource
// 1.指定pickerview有几个表盘(几列)
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    if (self.showType == MLDatePickerModeYMD) {
        return 3;
    } else if (self.showType == MLDatePickerModeYM) {
        return 2;
    }
    return 0;
}

// 2.指定每个表盘上有几行数据
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    NSArray *rowsArr = [NSArray array];
    if (self.showType == MLDatePickerModeYMD) {
        rowsArr = @[@(self.yearArr.count), @(self.monthArr.count), @(self.dayArr.count)];
    } else if (self.showType == MLDatePickerModeYM) {
        rowsArr = @[@(self.yearArr.count), @(self.monthArr.count)];
    }
    return [rowsArr[component] integerValue];
}

#pragma mark - UIPickerViewDelegate
// 3.设置 pickerView 的 显示内容
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view {
    // 设置分割线的颜色
    ((UIView *)[pickerView.subviews objectAtIndex:1]).backgroundColor = [UIColor colorWithRed:195/255.0 green:195/255.0 blue:195/255.0 alpha:1.0f];
    ((UIView *)[pickerView.subviews objectAtIndex:2]).backgroundColor = [UIColor colorWithRed:195/255.0 green:195/255.0 blue:195/255.0 alpha:1.0f];
    
    UILabel *label = (UILabel *)view;
    if (!label) {
        label = [[UILabel alloc]init];
        label.backgroundColor = [UIColor clearColor];
        label.textAlignment = NSTextAlignmentCenter;
        label.font = font(24);
        // 字体自适应属性
        label.adjustsFontSizeToFitWidth = YES;
        // 自适应最小字体缩放比例
        label.minimumScaleFactor = 0.5f;
    }
    // 给选择器上的label赋值
    [self setDateLabelText:label component:component row:row];
    return label;
}

// 4.选中时回调的委托方法,在此方法中实现省份和城市间的联动
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    // 获取滚动后选择的日期
    self.selectDate = [self getDidSelectedDate:component row:row];
    NSString *selectDateValue = [NSDate br_getDateString:self.selectDate format:self.selectDateFormatter];
    switch (self.showType) {
        case MLDatePickerModeYMD:{
            if(self.tag == 101){
                self.startDayTF.text = selectDateValue;
            }else if(self.tag == 102){
                self.endDayTF.text = selectDateValue;
            }
        }
            break;
        case MLDatePickerModeYM:{
            self.monthTF.text = selectDateValue;
        }
            break;
            
        default:
            break;
    }
    
}

// 设置行高
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
    return 35.0f;
}

- (void)setDateLabelText:(UILabel *)label component:(NSInteger)component row:(NSInteger)row {
    if (self.showType == MLDatePickerModeYMD) {
        if (component == 0) {
            label.text = [NSString stringWithFormat:@"%@年", self.yearArr[row]];
        } else if (component == 1) {
            label.text = [NSString stringWithFormat:@"%@月", self.monthArr[row]];
        } else if (component == 2) {
            label.text = [NSString stringWithFormat:@"%@日", self.dayArr[row]];
        }
    } else if (self.showType == MLDatePickerModeYM) {
        if (component == 0) {
            label.text = [NSString stringWithFormat:@"%@年", self.yearArr[row]];
        } else if (component == 1) {
            label.text = [NSString stringWithFormat:@"%@月", self.monthArr[row]];
        }
    }
}

- (NSDate *)getDidSelectedDate:(NSInteger)component row:(NSInteger)row {
    NSString *selectDateValue = nil;
    if (self.showType == MLDatePickerModeYMD) {
        if (component == 0) {
            _yearIndex = row;
            [self updateDateArray];
            [self.YMpickView reloadComponent:1];
            [self.YMpickView reloadComponent:2];
        } else if (component == 1) {
            _monthIndex = row;
            [self updateDateArray];
            [self.YMpickView reloadComponent:2];
        } else if (component == 2) {
            _dayIndex = row;
        }
        selectDateValue = [NSString stringWithFormat:@"%@-%02ld-%02ld", self.yearArr[_yearIndex], [self.monthArr[_monthIndex] integerValue], [self.dayArr[_dayIndex] integerValue]];
    } else if (self.showType == MLDatePickerModeYM) {
        if (component == 0) {
            _yearIndex = row;
            [self updateDateArray];
            [self.YMpickView reloadComponent:1];
        } else if (component == 1) {
            _monthIndex = row;
        }
        selectDateValue = [NSString stringWithFormat:@"%@-%02ld", self.yearArr[_yearIndex], [self.monthArr[_monthIndex] integerValue]];
    }
    return [NSDate br_getDate:selectDateValue format:self.selectDateFormatter];
}


#pragma mark - getter 方法
- (NSArray *)yearArr {
    if (!_yearArr) {
        _yearArr = [NSArray array];
    }
    return _yearArr;
}

- (NSArray *)monthArr {
    if (!_monthArr) {
        _monthArr = [NSArray array];
    }
    return _monthArr;
}

- (NSArray *)dayArr {
    if (!_dayArr) {
        _dayArr = [NSArray array];
    }
    return _dayArr;
}

#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    self.tag = textField.tag;
    return NO; // 当前 textField 不可编辑,可以响应点击事件
}

//按日/月切换
- (IBAction)change:(UIButton *)sender {
    if (self.showType == MLDatePickerModeYM) {
        self.showType = MLDatePickerModeYMD;
    }else{
        self.showType = MLDatePickerModeYM;
    }
    [self setUP];
    
    NSString *str = (self.showType == MLDatePickerModeYM)?@"按月选择":@"按日选择";
    [sender setTitle:str forState:(UIControlStateNormal)];
}

//完成
- (IBAction)done:(id)sender {
    NSString *startTime = @"";
    NSString *endTime = @"";
    NSInteger dateType = (self.showType == MLDatePickerModeYM) ? 1 : 2;
    
    if((self.showType == MLDatePickerModeYM)){
        if (![self.monthTF.text notNilOrEmpty]) {
            return;
        }
        startTime = endTime = self.monthTF.text;
        self.block(dateType, startTime, endTime);
        [self.navigationController popViewControllerAnimated:YES];
    }else{
        
        if ((![self.startDayTF.text notNilOrEmpty])||(![self.endDayTF.text notNilOrEmpty])) {
            return;
        }
        NSDate *start = [NSDate br_getDate:self.startDayTF.text format:self.selectDateFormatter];
        NSDate *end = [NSDate br_getDate:self.endDayTF.text format:self.selectDateFormatter];
        BOOL minMoreThanMax = [start br_compare:end format:self.selectDateFormatter] == NSOrderedDescending;
        if (minMoreThanMax) {
            SVHUD_HINT(@"开始时间不得大于结束时间!");
        }else{
            startTime = self.startDayTF.text;
            endTime = self.endDayTF.text;
            
            self.block(dateType, startTime, endTime);
            [self.navigationController popViewControllerAnimated:YES];
        }
        
    }  
}

@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271