自定义时间选择器,带有年月日的时间选择器

"没成功说明不够努力"

Posted by 李勇 on 2017-06-25

从此踏上了Swift的学习道路

在用Swift重写公司的项目中我计划先写一个工具类的三方库,主要集中我平时在项目中用到的工具类,点击查看库文件LYTools,目前还处于开始阶段,之前用的OC的比较方便的库在此慢慢都用Swift重写一遍。

正文

此处记录一个时间选择的工具,可以按照年月日时分选择,直接看效果图吧,图片前面是使用代码,这里使用的block,也可以选择使用代理方法

1
2
3
4
let pickView = LYDatePicker.init(component: 2)
pickView.ly_datepickerWithFiveComponent = {(date:Date,year:NSInteger,month:NSInteger,day:NSInteger,hour:NSInteger,minute:NSInteger) -> Void in
}
pickView.show()

image

1
2
3
4
let pickView = LYDatePicker.init(component: 3)
pickView.ly_datepickerWithFiveComponent = {(date:Date,year:NSInteger,month:NSInteger,day:NSInteger,hour:NSInteger,minute:NSInteger) -> Void in
}
pickView.show()

image

1
2
3
4
let pickView = LYDatePicker.init(component: 4)
pickView.ly_datepickerWithFiveComponent = {(date:Date,year:NSInteger,month:NSInteger,day:NSInteger,hour:NSInteger,minute:NSInteger) -> Void in
}
pickView.show()

image

1
2
3
4
let pickView = LYDatePicker.init(component: 5)
pickView.ly_datepickerWithFiveComponent = {(date:Date,year:NSInteger,month:NSInteger,day:NSInteger,hour:NSInteger,minute:NSInteger) -> Void in
}
pickView.show()

image

源码

首先需要一个Date的扩展文件“Date+Category.swift”,主要提供时间格式转换以及特殊时间取值等,下面列举使用到的扩展方法,想要查看更多可以到LYTools下载源码,好用请start,也欢迎来讨论技术

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
extension Date{
//本年
static func currentYear() -> NSInteger {
let components = calendar.dateComponents(unitFlags, from: Date())
return components.year!
}
//本月
static func currentMonth() -> NSInteger {
let components = calendar.dateComponents(unitFlags, from: Date())
return components.month!
}
//本日
static func currentDay() -> NSInteger {
let components = calendar.dateComponents(unitFlags, from: Date())
return components.day!
}
//当前小时
static func currentHour() -> NSInteger {
let components = calendar.dateComponents(unitFlags, from: Date())
return components.hour!
}
//当前分钟
static func currentMinute() -> NSInteger {
let components = calendar.dateComponents(unitFlags, from: Date())
return components.minute!
}
//时间字符串转换为日期
static func dateFromDateString(format: String, dateString: String) -> Date {
if format.isEmpty || dateString.isEmpty{
return Date()
}
let whiteSpace = CharacterSet.whitespacesAndNewlines
if format.trimmingCharacters(in: whiteSpace).isEmpty || dateString.trimmingCharacters(in: whiteSpace).isEmpty{
return Date()
}
if format == "0" || dateString == "0"{
return Date()
}
let dateFormat = DateFormatter()
dateFormat.dateFormat = format
let date = dateFormat.date(from: dateString)
if date != nil{
return date!
}
return Date()
}
static func dayCountInYearAndMonth(year:NSInteger, month:NSInteger) -> Int{
switch month {
case 1,3,5,7,8,10,12:
return 31
case 4,6,9,11:
return 30
case 2:
if year % 400 == 0 || (year % 100 != 0 && year % 4 == 0){
return 29
}else{
return 28
}
default:
return 0
}
}
}

下面是主要代码,时间显示上主要注意月,日的特殊处理,难点主要在于时间选择后如何能够避免某年某月中日的更换。此工具类通过代理或者block均可获取到选择的时间,下面的代码中直接复制使用会出现问题是因为我在写这个项目中使用了另一个工具类“UIView+Category.Swift”来直接赋值view的x或者y等,稍微修改后即可正常使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
import UIKit
@objc
protocol LYDatePickerDelete : NSObjectProtocol{
@objc func ly_datePickerChoosedTime(date:Date)
}
class LYDatePicker: UIControl {
var component : Int = 3
var delegate : LYDatePickerDelete?
var date = Date()
var year = Date.currentYear()
var month = Date.currentMonth()
var day = Date.currentDay()
var hour = Date.currentHour()
var minute = Date.currentMinute()
let yearEerliest = 1900//年份从1900年开始
let yearSum = 200//年份一共有200个选择
//点击确定后的block回调
typealias OneComponentBlock = (Date,NSInteger) -> Void
var ly_datepickerWithOneComponent : OneComponentBlock?
typealias TwoComponentBlock = (Date,NSInteger,NSInteger) -> Void
var ly_datepickerWithTwoComponent : TwoComponentBlock?
typealias ThreeComponentBlock = (Date,NSInteger,NSInteger,NSInteger) -> Void
var ly_datepickerWithThreeComponent : ThreeComponentBlock?
typealias FourComponentBlock = (Date,NSInteger,NSInteger,NSInteger,NSInteger) -> Void
var ly_datepickerWithFourComponent : FourComponentBlock?
typealias FiveComponentBlock = (Date,NSInteger,NSInteger,NSInteger,NSInteger,NSInteger) -> Void
var ly_datepickerWithFiveComponent : FiveComponentBlock?
fileprivate lazy var subView : UIView = {
let view = UIView(frame:CGRect(x:0,y:kScreenH - 230 - 44,width:kScreenW,height:230 + 44))
view.backgroundColor = UIColor.white
return view
}()
fileprivate lazy var pickerView : UIPickerView = {
let pickerView = UIPickerView(frame:CGRect(x:0,y:44,width:kScreenW,height:230))
pickerView.backgroundColor = UIColor.white
pickerView.delegate = self
pickerView.dataSource = self
return pickerView
}()
fileprivate lazy var sureBtn : UIButton = {
let btn = UIButton(type:.custom)
btn.setTitle("确定", for: .normal)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 15.0)
btn.setTitleColor(UIColor.RGBS(s: 33), for: .normal)
btn.addTarget(self, action: #selector(LYDatePicker.sureAction), for: .touchUpInside)
return btn
}()
fileprivate lazy var cancleBtn : UIButton = {
let btn = UIButton(type:.custom)
btn.setTitle("取消", for: .normal)
btn.titleLabel?.font = UIFont.systemFont(ofSize: 15.0)
btn.setTitleColor(UIColor.RGBS(s: 33), for: .normal)
btn.addTarget(self, action: #selector(LYDatePicker.cancleAction), for: .touchUpInside)
return btn
}()
fileprivate lazy var titleLbl : UILabel = {
let lbl = UILabel()
lbl.textAlignment = .center
lbl.textColor = UIColor.RGBS(s: 33)
lbl.font = UIFont.systemFont(ofSize: 15.0)
lbl.text = "请选择时间"
return lbl
}()
init(component:Int) {
if component > 5{
self.component = 5
}else{
self.component = component
}
super.init(frame:CGRect(x:0,y:0,width:kScreenW,height:kScreenH))
self.setUpGlobalViews()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension LYDatePicker{
func setUpGlobalViews() {
//1.点击背景取消选择
self.backgroundColor = UIColor.clear
let bg_btn = UIButton(frame:self.bounds)
bg_btn.backgroundColor = UIColor.black
bg_btn.alpha = 0.3
self.addSubview(bg_btn)
bg_btn.addTarget(self, action: #selector(LYDatePicker.cancleAction), for: .touchDown)
//2.时间选择器
self.subView.addSubview(self.pickerView)
//年是从1900开始计算,时和分都是从0开始计算,尾数为0,因此索引正常;月和日从1计算,因此索引要减去1
self.pickerView.selectRow(self.year - yearEerliest, inComponent: 0, animated: false)
if self.component > 1{
self.pickerView.selectRow(self.month - 1, inComponent: 1, animated: false)
}
if self.component > 2{
self.pickerView.selectRow(self.day - 1, inComponent: 2, animated: false)
}
if self.component > 3{
self.pickerView.selectRow(self.hour, inComponent: 3, animated: false)
}
if self.component > 4{
self.pickerView.selectRow(self.minute, inComponent: 4, animated: false)
}
//3.确定,取消按钮
let btnView = UIView(frame:CGRect(x:0,y:0,width:kScreenW,height:44))
self.cancleBtn.frame = CGRect(x:10,y:5,width:60,height:35)
self.titleLbl.frame = CGRect(x:80,y:5,width:kScreenW-160,height:35)
self.sureBtn.frame = CGRect(x:kScreenW-60-10,y:5,width:60,height:35)
let line = UIView(frame:CGRect(x:0,y:43,width:kScreenW,height:1))
line.backgroundColor = UIColor.RGBS(s: 240)
btnView.backgroundColor = UIColor.white
btnView.addSubview(cancleBtn)
btnView.addSubview(titleLbl)
btnView.addSubview(sureBtn)
btnView.addSubview(line)
self.subView.addSubview(btnView)
}
func sureAction() {
if (self.ly_datepickerWithOneComponent != nil) {
self.ly_datepickerWithOneComponent!(self.date,self.year)
}
if (self.ly_datepickerWithTwoComponent != nil) {
self.ly_datepickerWithTwoComponent!(self.date,self.year,self.month)
}
if (self.ly_datepickerWithThreeComponent != nil) {
self.ly_datepickerWithThreeComponent!(self.date,self.year,self.month,self.day)
}
if (self.ly_datepickerWithFourComponent != nil) {
self.ly_datepickerWithFourComponent!(self.date,self.year,self.month,self.day,self.hour)
}
if (self.ly_datepickerWithFiveComponent != nil) {
self.ly_datepickerWithFiveComponent!(self.date,self.year,self.month,self.day,self.hour,self.minute)
}
if (self.delegate != nil){
self.delegate?.ly_datePickerChoosedTime(date: self.date)
}
}
func cancleAction() {
UIApplication.shared.keyWindow?.addSubview(self)
UIApplication.shared.keyWindow?.bringSubview(toFront: self)
UIView.animate(withDuration: 0.25, animations: {
self.subView.y = kScreenH
}){(completion) in
self.removeFromSuperview()
}
}
func show() {
UIApplication.shared.keyWindow?.addSubview(self)
UIApplication.shared.keyWindow?.bringSubview(toFront: self)
self.subView.y = kScreenH
self.addSubview(self.subView)
UIView.animate(withDuration: 0.25, animations: {
self.subView.y = kScreenH - 230 - 44
})
}
}
extension LYDatePicker : UIPickerViewDelegate,UIPickerViewDataSource{
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return self.component
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch component {
case 0:
return yearSum
case 1:
return 12
case 2:
return Date.dayCountInYearAndMonth(year: self.year, month: self.month)
case 3:
return 24
case 4:
return 60
default:
return 0
}
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
let lbl = UILabel(frame:CGRect(x:0, y:0, width:kScreenW / 6.0, height:30))
lbl.textColor = UIColor.RGBS(s: 33)
lbl.textAlignment = .center
lbl.font = UIFont.systemFont(ofSize: 14.0)
switch component {
case 0:
lbl.text = "\(row + self.yearEerliest)年"
case 1:
lbl.text = "\(row + 1)月"
case 2:
lbl.text = "\(row + 1)日"
case 3:
lbl.text = "\(row)时"
case 4:
lbl.text = "\(row)分"
default:
lbl.text = ""
}
return lbl
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if component == 0 || component == 1{
if self.component > 2{
let nextYear = self.pickerView.selectedRow(inComponent: 0)
let nextMonth = self.pickerView.selectedRow(inComponent: 1) + 1
if self.day == 31{
if [4,6,9,11].contains(nextMonth){
self.pickerView.selectRow(29, inComponent: 2, animated: false)
}else if nextMonth == 2{
if Date.dayCountInYearAndMonth(year: nextYear, month: nextMonth) == 28{
self.pickerView.selectRow(27, inComponent: 2, animated: false)
}else{
self.pickerView.selectRow(28, inComponent: 2, animated: false)
}
}
}else if self.day == 30 || self.day == 29{
if nextMonth == 2{
if Date.dayCountInYearAndMonth(year: nextYear, month: nextMonth) == 28{
self.pickerView.selectRow(27, inComponent: 2, animated: false)
}else{
self.pickerView.selectRow(28, inComponent: 2, animated: false)
}
}
}
}
}
self.resetDate()
}
func resetDate() {
self.year = self.pickerView.selectedRow(inComponent: 0) + yearEerliest
if self.component > 1{
self.month = self.pickerView.selectedRow(inComponent: 1) + 1
}
if self.component > 2{
self.day = self.pickerView.selectedRow(inComponent: 2) + 1
}
if self.component > 3{
self.hour = self.pickerView.selectedRow(inComponent: 3)
}
if self.component > 4{
self.minute = self.pickerView.selectedRow(inComponent: 4)
}
self.date = Date.dateFromDateString(format: "yyyy-MM-dd HH:mm", dateString: "\(self.year)-\(self.month)-\(self.day) \(self.hour):\(self.minute)")
self.pickerView.reloadAllComponents()
}
}

注明

还有OC版的同样的时间选择器,这里就不放出来了,如过有需要可联系我,坚持开源,免费分享