在写项目时,个人资料生日这一块,想写成QQ中使用的效果。然后就学了一下UIPickerView。

ios自定义uiview Ios自定义时间_开发语言

系统其实提供了一个日期选择器UIDatePicker,但是它的实现效果并不是我想要的

ios自定义uiview Ios自定义时间_ios自定义uiview_02


所以我自定义选择器来实现一个日期选择器。

设计思路

为了在点击时,背景为灰色,所以我打算将一个背景为灰色的图层加到主页面上,点击确定、取消和空白区域可以移除。再向此图层上加一个contentView。这个contentView上放取消按钮和确认按钮以及选择器。

代码:

ios自定义uiview Ios自定义时间_objective-c_03

BasePickerView.h
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface BasePickerView : UIView
@property (nonatomic, strong) UIView* contentView;
//选择器
@property (nonatomic, strong)UIPickerView *pickerView;
//取消按钮
@property (nonatomic, strong)UIButton *cancelButton;
 //确定按钮
@property (nonatomic, strong)UIButton *confirmButton;
//选择器的高度
@property (nonatomic, assign)CGFloat pickerViewHeight;

//创建视图,初始化视图时初始数据
- (void)initPickView;

//确认按钮的点击事件
- (void)clickConfirmButton;

//pickerView的显示
- (void)show;

//移除pickerView
- (void)disMiss;

@end

NS_ASSUME_NONNULL_END
//BasePickerView.m
#import "BasePickerView.h"
#define ScreenWidth [UIScreen mainScreen].bounds.size.width
#define ScreenHeight [UIScreen mainScreen].bounds.size.height
@implementation BasePickerView

- (instancetype)init {
    self = [super init];
    
    if(self) {
        _pickerViewHeight = 250;
        
        //设置此图层大小为主屏幕大小
        self.bounds = [UIScreen mainScreen].bounds;
        //设置背景为灰色
        self.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.6];
        
        //定义手势,在点击空白区域时,移除此图层
        UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(disMiss)];
        self.userInteractionEnabled = YES;
        [self addGestureRecognizer:tap];
        
        //将内容视图加入此图层以及将选择器、确定按钮、取消按钮加入内容视图
        [self addSubview:self.contentView];
        [self.contentView addSubview:self.pickerView];
        [self.contentView addSubview:self.cancelButton];
        [self.contentView addSubview:self.confirmButton];
        
        //以便在子类中重写此方法,将pickerView所需的数据初始化
        [self initPickView];
    }
    
    return self;
}

- (void)initPickView {
    
}

//初始化内容视图
- (UIView *)contentView
{
    if (!_contentView) {
 
        _contentView = [[UIView alloc]initWithFrame:CGRectMake(0, ScreenHeight, ScreenWidth, self.pickerViewHeight)];
        [_contentView setBackgroundColor:[UIColor whiteColor]];
    }
    return _contentView;
}

//初始化选择器
- (UIPickerView *)pickerView
{
    if (!_pickerView) {
  
        _pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0,  0, self.contentView.frame.size.width, self.contentView.frame.size.height)];
        [_pickerView setBackgroundColor:[UIColor whiteColor]];
         
    }
    return _pickerView;
}
//初始化取消按钮
- (UIButton *)cancelButton {
    if (!_cancelButton) {
      
        _cancelButton = [[UIButton alloc]initWithFrame:CGRectMake(16, 0, 44, 44)];
        [_cancelButton setTitle:@"取消" forState:UIControlStateNormal];
        [_cancelButton setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
  
        [_cancelButton.titleLabel setFont:[UIFont systemFontOfSize:16]];
        [_cancelButton addTarget:self action:@selector(clickCancelButton) forControlEvents:UIControlEventTouchUpInside];
    }
    return _cancelButton;
}
//初始化确定按钮
- (UIButton *)confirmButton {
    if (!_confirmButton) {
    
        _confirmButton = [[UIButton alloc]initWithFrame:CGRectMake(self.contentView.frame.size.width - self.cancelButton.frame.size.width - self.cancelButton.frame.origin.x, self.cancelButton.frame.origin.y, self.cancelButton.frame.size.width, self.cancelButton.frame.size.height)];
        [_confirmButton setTitle:@"确定" forState:UIControlStateNormal];
        [_confirmButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
      
        [_confirmButton.titleLabel setFont:[UIFont systemFontOfSize:16]];
        [_confirmButton addTarget:self action:@selector(clickConfirmButton) forControlEvents:UIControlEventTouchUpInside];
    }
    return _confirmButton;
}

//等待继承他的子类重写此方法
- (void)clickConfirmButton {
    [self disMiss];
}

- (void)clickCancelButton {
    [self disMiss];
}

//获取keyWindow
- (UIWindow *)getKeyWindow {
    if (@available(iOS 13.0, *)) {
        for (UIWindowScene* windowScene in [UIApplication sharedApplication].connectedScenes) {
            if (windowScene.activationState == UISceneActivationStateForegroundActive) {
                for (UIWindow *window in windowScene.windows) {
                    if (window.isKeyWindow)
                    {
                        return window;
                        break;
                    }
                }
            }
        }
    } else {
        return [UIApplication sharedApplication].keyWindow;
    }
    return nil;
}
//移除此图层
- (void)disMiss {
    CGRect frameContent =  self.contentView.frame;
    
//    相当于把contentView的y坐标设置为ScreenHeight,达到一个隐藏的目的
    frameContent.origin.y += self.contentView.frame.size.height;
    //设置动画效果
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
        //将此图层不透明性设为0
        [self.layer setOpacity:0];
//        再将内容视图隐藏
        self.contentView.frame = frameContent;
    } completion:^(BOOL finished) {
        //将此图层从父视图中移除掉
        [self removeFromSuperview];
    }];
    
}

//推出此图层
- (void)show {
    [[self getKeyWindow] addSubview:self];
    [self setCenter:[self getKeyWindow].center];
    [[self getKeyWindow] bringSubviewToFront:self];
    
    CGRect frameContent =  self.contentView.frame;
 
    frameContent.origin.y -= self.contentView.frame.size.height;
    [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
        [self.layer setOpacity:1.0];
        self.contentView.frame = frameContent;
            
    } completion:nil];
}

@end

新建一个继承于BasePickerView的类,在这个类里实现UIPickerView数据源和代理方法,以及重写一些方法。

#import "BasePickerView.h"

NS_ASSUME_NONNULL_BEGIN

@class BasePickerView;
@protocol  DatePickerViewDelegate<NSObject>   //定义一个代理。
//此方法目的是得到当前选择器的选择结果
- (void)pickerDateView:(BasePickerView *)pickerDateView selectYear:(NSInteger)year selectMonth:(NSInteger)month selectDay:(NSInteger)day;

@end
@interface DatePickerView : BasePickerView
<UIPickerViewDelegate,
UIPickerViewDataSource>
@property(nonatomic, weak)id <DatePickerViewDelegate>delegate ;

/** 选择的年 */
@property (nonatomic, assign)NSInteger selectYear;
/** 选择的月 */
@property (nonatomic, assign)NSInteger selectMonth;
/** 选择的日 */
@property (nonatomic, assign)NSInteger selectDay;
//现在的年月日
@property (nonatomic, assign)NSInteger currentYear;
@property (nonatomic, assign)NSInteger currentMonth;
@property (nonatomic, assign)NSInteger currentDay;
//默认年月日
@property (nonatomic, assign)NSInteger defaultYear;
@property (nonatomic, assign)NSInteger defaultMonth;
@property (nonatomic, assign)NSInteger defaultDay;

//显示的最低年
@property (nonatomic, assign)NSInteger minShowYear;

@property (nonatomic, assign)NSInteger yearSum;

-(void)setDefaultTSelectYear:(NSInteger)defaultSelectYear defaultSelectMonth:(NSInteger)defaultSelectMonth defaultSelectDay:(NSInteger)defaultSelectDay;

@end
NS_ASSUME_NONNULL_END
#import "DatePickerView.h"

@implementation DatePickerView

- (void)initPickView {
    [super initPickView];
    
    //设置数据
    _minShowYear = 1940;//最小年份
    NSCalendar *calendar = [[NSCalendar alloc]
                             initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    // 获取当前日期
    NSDate* date = [NSDate date];
    // 指定获取指定年、月、日、时、分、秒的信息
    unsigned unitFlags = NSCalendarUnitYear |
    NSCalendarUnitMonth |  NSCalendarUnitDay |
    NSCalendarUnitHour |  NSCalendarUnitMinute |
    NSCalendarUnitSecond | NSCalendarUnitWeekday;
    // 获取不同时间字段的信息
    NSDateComponents* comp = [calendar components:unitFlags fromDate:date];
    
    _yearSum = comp.year-_minShowYear+1;
    _currentYear = comp.year;
    _currentMonth = comp.month;
    _currentDay = comp.day;
 
    
    _selectYear  = comp.year;
    _selectMonth = comp.month;
    _selectDay = comp.day;
    _defaultYear = comp.year;
    _defaultMonth = comp.month;
    _defaultDay = comp.day;
    [self.pickerView setDelegate:self];
    [self.pickerView setDataSource:self];
}

- (void)setDefaultTSelectYear:(NSInteger)defaultSelectYear defaultSelectMonth:(NSInteger)defaultSelectMonth defaultSelectDay:(NSInteger)defaultSelectDay {
    if (defaultSelectYear != 0) {
     _defaultYear=defaultSelectYear;
    }
    
    if (defaultSelectMonth != 0) {
        _defaultMonth = defaultSelectMonth;
    }
    
    if (defaultSelectDay != 0) {
         _defaultDay=defaultSelectDay;
    }
    if (defaultSelectYear == -1) {
        _defaultYear=_currentYear+1;
        _defaultMonth=1;
        _defaultDay=1;
    }
   
   
    [self.pickerView selectRow:(_defaultYear - _minShowYear) inComponent:0 animated:NO];

    [self.pickerView selectRow:(_defaultMonth - 1) inComponent:1 animated:NO];
    [self.pickerView reloadComponent:1];
    [self.pickerView selectRow:(_defaultDay-1) inComponent:2 animated:NO];
    [self.pickerView reloadComponent:2];
           
    [self refreshPickViewData];
}

//返回列数
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 3;
}

- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
    //每一行的高度
    return 40;
}

//返回行数
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    if (component == 0) {
        return self.yearSum;
    } else if(component == 1) {
        NSInteger yearSelected = [pickerView selectedRowInComponent:0] + self.minShowYear;
            return 12;
    } else {
    	NSInteger yearSelected = [pickerView selectedRowInComponent:0] + self.minShowYear;
        NSInteger monthSelected = [pickerView selectedRowInComponent:1] + 1;
        return  [self getDaysWithYear:yearSelected month:monthSelected];
    }
}

- (NSInteger)getDaysWithYear:(NSInteger)year month:(NSInteger)month {
    switch (month) {
        case 1:
            return 31;
            break;
        case 2:
            //分为闰年和平年
            if (year % 400 == 0 || (year % 100 != 0 && year % 4 == 0)) {
                return 29;
            }else{
                return 28;
            }
            break;
        case 3:
            return 31;
            break;
        case 4:
            return 30;
            break;
        case 5:
            return 31;
            break;
        case 6:
            return 30;
            break;
        case 7:
            return 31;
            break;
        case 8:
            return 31;
            break;
        case 9:
            return 30;
            break;
        case 10:
            return 31;
            break;
        case 11:
            return 30;
            break;
        case 12:
            return 31;
            break;
        default:
            return 0;
            break;
    }
}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    
    NSInteger selectYear;
    NSInteger selectMonth;
    
    switch (component) {
        case 0:
            [pickerView reloadComponent:1];
            selectYear = row + _minShowYear;
            [pickerView reloadComponent:2];
            break;
        case 1:
            selectMonth = row + 1;
            [pickerView reloadComponent:2];
        default:
            break;
    }
    [self refreshPickViewData];
}
//更新数据
- (void)refreshPickViewData {
    self.selectYear  = [self.pickerView selectedRowInComponent:0] + self.minShowYear;
    self.selectMonth = [self.pickerView selectedRowInComponent:1] + 1;
    self.selectDay   = [self.pickerView selectedRowInComponent:2] + 1;
}

- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(nullable UIView *)view {
    
    NSString *text;
    if (component == 0) {
        text =  [NSString stringWithFormat:@"%zd年", row + _minShowYear];
    } else if (component == 1) {
        text =  [NSString stringWithFormat:@"%zd月", row + 1];
    } else {
        text = [NSString stringWithFormat:@"%zd日", row + 1];
    }
    
    UILabel *label = [[UILabel alloc]init];
    label.textAlignment = 1;
    label.font = [UIFont systemFontOfSize:16];
    label.text = text;
    
    return label;
}

- (void)clickConfirmButton {
    if ([self.delegate respondsToSelector:@selector(pickerDateView:selectYear:selectMonth:selectDay:)]) {
        [self.delegate pickerDateView:self selectYear:self.selectYear selectMonth:self.selectMonth selectDay:self.selectDay];
    }
    //为了保留dismiss操作
    [super clickConfirmButton];
    
}
@end
//viewController.m
#import <UIKit/UIKit.h>
#import "DatePickerView.h"
@interface ViewController : UIViewController
<DatePickerViewDelegate>
@end
//viewController.h
#import "ViewController.h"

@interface ViewController () {
    UIButton* dateButton;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    dateButton = [UIButton buttonWithType:UIButtonTypeCustom];
    dateButton.frame = CGRectMake(50, 100, 200, 50);
    [dateButton setTitle:@"选择日期" forState:UIControlStateNormal];
    [self.view addSubview:dateButton];
    [dateButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    [dateButton addTarget:self action:@selector(pickViewSelect) forControlEvents:UIControlEventTouchUpInside];
}

- (void)pickViewSelect {
    DatePickerView* datePickerView = [[DatePickerView alloc] init];
    datePickerView.delegate = self;
    [datePickerView setDefaultTSelectYear:2001 defaultSelectMonth:3 defaultSelectDay:23];
    [datePickerView show];
}

- (void)pickerDateView:(BasePickerView *)pickerDateView selectYear:(NSInteger)year selectMonth:(NSInteger)month selectDay:(NSInteger)day {
    NSString* string = [NSString stringWithFormat:@"%ld年%ld月%ld日", year, month, day];
    [dateButton setTitle:string forState:UIControlStateNormal];
}
@end

效果:

ios自定义uiview Ios自定义时间_图层_04