swift 自定义DatePacker

请添加图片描述

import Foundation

enum AppDatePickerStyle {
    
    case KDatePickerDate    //年月日
    case KDatePickerTime    //年月日时分
    case kDatePickerMonth // 年月
    case KDatePickerSecond  //秒

}

class AppDatePicker: UIView {

    private let jk_rootView = UIApplication.shared.keyWindow!
    private var jk_backgroundView: UIView!
    
    private var confirmHandler: (( _ year: Int, _ month: Int,_ day: Int) -> Void)?//选择的回调
    var cancelHandler: (() -> Void)?//取消的回调
    /// 选择器类型
    fileprivate var datePickerStyle: AppDatePickerStyle!
    /// 时间数据
    fileprivate var unitFlags:Set<Calendar.Component>!
    var pickerView = UIPickerView()
    
    //数据相关
    fileprivate var yearRange = 30 + 1000//年的范围
    
    fileprivate var dayRange = 0 //
    
    fileprivate var startYear = 0
    
    // 当前选中日期
    fileprivate var selectedYear = 0;
    fileprivate var selectedMonth = 0;
    fileprivate var selectedDay = 0;
    fileprivate var selectedHour = 0;
    fileprivate var selectedMinute = 0;
    fileprivate var selectedSecond = 0;

    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    convenience init(title: String = "选择日期", type: AppDatePickerStyle, confirmHandler: @escaping (_ year:Int,_ month:Int,_ day:Int) -> Void) {
        self.init(frame: .zero)
        
        configUI()

        self.confirmHandler = confirmHandler
        
        jk_titleLabel.text = title
        initDatePickerWithType(type: type)
    }
    convenience init(title: String = "选择日期", year: Int, month: Int, day: Int, confirmHandler: @escaping (_ year:Int,_ month:Int,_ day:Int) -> Void) {
        self.init(frame: .zero)
        
        configUI()
        self.confirmHandler = confirmHandler
        
        jk_titleLabel.text = title
        initDatePickerWithType(type: .kDatePickerMonth, date: year == -1 ? nil:Date.dateFor("\(year)-\(month)-\(day)"))
    }
    
    private func getHeight() -> CGFloat {
        return CGFloat(56 + 4 * 50 + 56)
    }
    
    // MARK: - UIButtonAction
    @objc private func confimBtnAction() {
        
        if let holder = confirmHandler {
            holder(selectedYear, selectedMonth, selectedDay)
        }
        
        hide()
    }
    
    // MARK: - configUI
    func configUI() {
        
        
        // 防止多个 AlertView 重复显示
        if jk_rootView.subviews.filter({ $0.isKind(of: AppDatePicker.self) }).count > 0 { return }
                
        jk_backgroundView = UIView()
        jk_backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0)
        
        jk_rootView.addSubview(jk_backgroundView)
        jk_rootView.addSubview(self)
        addSubview(jk_backView)
        jk_backView.addSubview(cornerBackView)
        cornerBackView.addSubview(jk_titleLabel)
        cornerBackView.addSubview(pickerView)
        cornerBackView.addSubview(jk_cancelBtn)
        cornerBackView.addSubview(conformBtn)
        cornerBackView.addSubview(verticalLineView)
        
        jk_backgroundView.snp.makeConstraints { (make) in
            make.edges.equalTo(jk_rootView)
        }
        
        let height = getHeight()
        snp.makeConstraints { make in
            make.left.right.equalToSuperview()
            make.height.equalTo(height + Size.safeAreaBottomGap)
            make.bottom.equalTo(height + Size.safeAreaBottomGap)
        }
        jk_backView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        cornerBackView.snp.makeConstraints { make in
            make.left.top.right.equalToSuperview()
            make.height.equalTo(height)
        }
        jk_titleLabel.snp.makeConstraints { make in
            make.height.equalTo(40)
            make.top.equalTo(16)
            make.left.right.equalToSuperview()
        }
        pickerView.snp.makeConstraints { make in
            make.top.equalTo(jk_titleLabel.snp.bottom)
            make.left.right.equalToSuperview()
            make.height.equalTo(50 * 4)
        }
        jk_cancelBtn.snp.makeConstraints { make in
            make.height.equalTo(56)
            make.top.equalTo(pickerView.snp.bottom)
            make.left.equalToSuperview()
            make.width.equalToSuperview().multipliedBy(0.5)
        }
        conformBtn.snp.makeConstraints { make in
            make.right.equalToSuperview()
            make.centerY.height.width.equalTo(jk_cancelBtn)
        }
        verticalLineView.snp.makeConstraints { make in
            make.height.equalTo(20)
            make.width.equalTo(1)
            make.centerY.equalTo(jk_cancelBtn)
            make.centerX.equalToSuperview()
        }
        
        self.jk_rootView.layoutIfNeeded()
        
        let tapGR = UITapGestureRecognizer.init(target: self, action: #selector(hideAction))
        jk_backgroundView.addGestureRecognizer(tapGR)
        
        jk_cancelBtn.addTarget(self, action: #selector(hideAction), for: .touchUpInside)
        conformBtn.addTarget(self, action: #selector(confimBtnAction), for: .touchUpInside)
        
        pickerView.delegate = self
        pickerView.dataSource = self
        
    }
    private lazy var jk_backView: UIView = {
        let jk_backView = UIView()
        jk_backView.backgroundColor = .white
        return jk_backView
    }()
    private lazy var cornerBackView: UIView = {
        let cornerBackView = UIView()
        cornerBackView.backgroundColor = .white
        return cornerBackView
    }()
    private lazy var jk_titleLabel: UILabel = {
        let jk_titleLabel = UILabel()
        jk_titleLabel.textAlignment = .center
        jk_titleLabel.font = kSetPingFangMedium(18)
        return jk_titleLabel
    }()
    private lazy var jk_cancelBtn: UIButton = {
        let jk_cancelBtn = UIButton()
        jk_cancelBtn.setTitle("取消", for: .normal)
        jk_cancelBtn.setTitleColor( .black, for: .normal)
        return jk_cancelBtn
    }()
    private lazy var verticalLineView: UIView = {
        let verticalLineView = UIView()
        verticalLineView.backgroundColor = .jky_viewBackgroundColor
        return verticalLineView
    }()
    private lazy var conformBtn: UIButton = {
        let conformBtn = UIButton()
        conformBtn.setTitle("确定", for: .normal)
        conformBtn.setTitleColor( .black, for: .normal)
        return conformBtn
    }()
    
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        fatalError("init(coder:) has not been implemented")
    }
}

//MARK:初始化数据
extension AppDatePicker {
    
    fileprivate func initDatePickerWithType(type: AppDatePickerStyle, date: Date? = nil)  {
        
        datePickerStyle = type
        let  calendar0 = Calendar.init(identifier: .gregorian)//公历
        
        var comps = DateComponents()//一个封装了具体年月日、时秒分、周、季度等的类
        
        unitFlags = [.year , .month , .day]
        
        switch  datePickerStyle {
        case .KDatePickerDate:
            break
        case .KDatePickerTime:
            unitFlags = [.year , .month , .day , .hour , .minute ]
        case .kDatePickerMonth:
            unitFlags = [.year , .month]
        case .KDatePickerSecond:
            unitFlags = [.year , .month , .day , .hour , .minute ,.second]
        default:
            break
        }
        
//        comps = calendar0.dateComponents(unitFlags, from: date != nil ? date! : Date())
        comps = calendar0.dateComponents(unitFlags, from: date != nil ? date!:Date())

        
        startYear = comps.year! - 100
        
        dayRange = self.isAllDay(year: startYear, month: 1)
        
        yearRange = 30 + 1000;
        selectedYear = comps.year!;
        selectedMonth = comps.month!;
        
        self.pickerView.selectRow(selectedYear - startYear, inComponent: 0, animated: true)
        self.pickerView.selectRow(selectedMonth - 1, inComponent: 1, animated: true)
        if datePickerStyle != .kDatePickerMonth {
            selectedDay = comps.day!;
            self.pickerView.selectRow(selectedDay - 1, inComponent: 2, animated: true)
        }
        
        switch  datePickerStyle {
        case .KDatePickerDate:
            break
        case .KDatePickerTime:
            selectedHour = comps.hour!;
            selectedMinute = comps.minute!;
            
            self.pickerView.selectRow(selectedHour , inComponent: 3, animated: true)
            self.pickerView.selectRow(selectedMinute , inComponent: 4, animated: true)
        case .KDatePickerSecond:
            selectedHour = comps.hour!;
            selectedMinute = comps.minute!;
            selectedSecond = comps.second!;
            self.pickerView.selectRow(selectedHour , inComponent: 3, animated: true)
            self.pickerView.selectRow(selectedMinute , inComponent: 4, animated: true)
            self.pickerView.selectRow(selectedSecond, inComponent: 5, animated: true)
        default:
            break
        }
        self.pickerView.reloadAllComponents()
    }
    
    //MARK:计算每个月有多少天
    fileprivate func isAllDay(year:Int, month:Int) -> Int {
        
        var   day:Int = 0
        switch(month)
        {
        case 1,3,5,7,8,10,12:
            day = 31
        case 4,6,9,11:
            day = 30
        case 2:
            
            if(((year%4==0)&&(year%100==0))||(year%400==0))
            {
                day=29
            }
            else
            {
                day=28;
            }
            
        default:
            break;
        }
        return day;
    }
    
}

extension AppDatePicker : UIPickerViewDelegate,UIPickerViewDataSource {
    
    //返回UIPickerView当前的列数
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return unitFlags == nil ? 0 : unitFlags.count
    }
    
    //确定每一列返回的东西
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        
        switch component {
        case 0:
            return yearRange
        case 1:
            return 12
        case 2:
            return dayRange
        case 3:
            return 24
        case 4:
            return 60
        case 5:
            return 60
        default:
            return 0
        }
    }
    
    func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
        return 45
    }
    
    //返回一个视图,用来设置pickerView的每行显示的内容。
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        
        
        let label  = UILabel(frame: CGRect(x: screenWidth * CGFloat(component) / 6 , y: 0, width: screenWidth/6, height: 50))
        
        label.font = UIFont.boldSystemFont(ofSize: CGFloat(16))

        label.tag = component*100+row
        
        label.textAlignment = .center
        
        switch component {
        case 0:
            
            label.frame=CGRect(x:5, y:0,width:screenWidth/4.0, height:50);
            
            
            label.text="\(self.startYear + row)年";
            
        case 1:
            
            label.frame=CGRect(x:screenWidth/4.0, y:0,width:screenWidth/8.0, height:50);
            
            
            label.text="\(row + 1)月";
        case 2:
            
            label.frame=CGRect(x:screenWidth*3/8, y:0,width:screenWidth/8.0, height:50);
            
            
            label.text="\(row + 1)日";
        case 3:
            
            label.textAlignment = .right
            
            label.text="\(row )时";
        case 4:
            
            label.textAlignment = .right
            
            label.text="\(row )分";
        case 5:
            
            label.textAlignment = .right
            
            label.frame=CGRect(x:screenWidth/6, y:0,width:screenWidth/6.0 - 5, height:50);
            
            
            label.text="\(row )秒";
            
        default:
            label.text="\(row )秒";
        }
        
        return label
    }
    
    //当点击UIPickerView的某一列中某一行的时候,就会调用这个方法
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        switch component {
        case 0:
            self.selectedYear = self.startYear + row
            
            self.dayRange = self.isAllDay(year: self.startYear, month: self.selectedMonth)
            
            if datePickerStyle != .kDatePickerMonth {
                self.pickerView.reloadComponent(2)
            }
        case 1:
            self.selectedMonth =  row + 1
            
            self.dayRange = self.isAllDay(year: self.startYear, month: self.selectedMonth)
            
            if datePickerStyle != .kDatePickerMonth {
                self.pickerView.reloadComponent(2)
            }
        case 2:
            selectedDay = row + 1
        case 3:
            selectedHour = row
        case 4:
            selectedMinute = row
        case 5:
            selectedSecond = row
        default:
            selectedSecond = row
        }
    }
    
}

extension AppDatePicker: UIGestureRecognizerDelegate {
    
    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        
        if let view = touch.view, view.isDescendant(of: pickerView) {
            return false
        }
        
        return true
    }
    
    func show() {
        
        self.jk_backView.layerCornerRadius(16, [.topLeft,.topRight])
        UIView.animate(withDuration: 0.2, animations: {
            self.jk_backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0.5)
            self.snp.updateConstraints { make in
                make.bottom.equalTo(0)
            }
            self.jk_rootView.layoutIfNeeded()
        })

    }
    
    @objc private func hideAction() {
        if let holder = cancelHandler {
            holder()
        }
        hide()
    }
    
    private func hide(completion: (() -> Void)? = nil) {
        
        UIView.animate(withDuration: 0.2, animations: {
            self.jk_backgroundView.backgroundColor = UIColor.black.withAlphaComponent(0)
            self.snp.updateConstraints { make in
                make.bottom.equalTo(self.getHeight() + Size.safeAreaBottomGap)
            }
            self.jk_rootView.layoutIfNeeded()
        }) { finished in
            guard finished else { return }
            self.removeFromSuperview()
            self.jk_backgroundView.removeFromSuperview()
        }
        
    }
        
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值