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()
}
}
}
11-16