先上一张效果图(原谅我还是这么无聊~~,我觉得动态的可能更能展示作品的特性)
写这个时间选择器之前我还没写过相关的东西,没写过其实是瞎话,以前在项目中用过
UIDatePicker
,所以可自定义性不是很强,也没啥可说的,虽然在我们的项目中时间选择器很常见,但是认真研究的人就不知道了……,这次我也算是认真的看了一下UIPickerView
内部的组成。先说说内部的组成吧。
1 UIPickerView
内部组成
在xcode的可视化工具中你可以看到UIPikerView的结构如下:
当然如果,你也可以打印一下
UIPikerView
的子空间,会发现,它有三个子空间,其中两个是一样的,而且高度是0.67,所以我们可以猜测这个高度是0.67的view就是那两条灰色的分割线。不信你可以试试,简要做以下测试
self.picker.subviews[1].hidden = YES;
self.picker.subviews[2].hidden = YES;
此时,你会惊奇的发现两条灰色的分割线不见了,那么证实了我们猜测的是正确的,那么接下来往下走吧。再来看结构,如果说你嫌看结构图麻烦的话,你可以利用打印子控件的方式看查看其内部的控件,我是采用后者的,不过这里为了更加形象,我采用图形结合的方式来讲。如下图:
请原谅我的懒惰,这里我把结构图全部展开来讲吧,不再一张一张的贴图了,
- UIPickerView的suviews 展开这个后我们看到里面又有3个
UIPickerColumnView
,当然你可以利用日志打印一下,会发现前三个是完全一样的,我们可以猜测每一个代表着一列,不信你可以改变数据源试试,会发现猜测的正确性。 - 接下来我们进入到每一列
UIPickerColumnView
,发现有三个UIView(子控件),前两个完全一样,第三个不同,我猜测,第三个是中间行,也就是选择到日期的那一行 - 以此类推,你可以看到中间行里面有一个子控件,名字是
UIPickerTableView
,这是苹果内部私有类,在UIKit框架中我们找不到它的头文件,但是依旧不影响我们获取到它。
快上代码,不多啰嗦了
2 为时间选择器自定义分割线
我们可以拿到UIPickerTableView
然后利用runtime
机制取出其内部的属性,如果你有印象的话,我说过每个UIPickerColumnView
中有三个UIView
控件,每个UIView
控件中只有一个子控件UIPickerTableView
,而三个UIView
控件中的第3个才是我们选择时间的那一行。
上述话的意思简单翻译为
UIPickerColumnView
中UIView
控件数 =UIPickerTableView
数
我们可以拿到每一列component( 即每一UIPickerColumnView
列)中UIPickerTableView
的数目
int pickerTableViewNum = (int)self.picker.subviews[0].subviews[component].subviews.count;
遍历吧,记住第3个是选择日期的那一行
for (int index = 0; index<pickerTableViewNum; index++) {
//取出对应的UIPickerTableView
UIView *tableView = self.picker.subviews[0].subviews[component].subviews[index].subviews[0];
//取出UIPickerTableView内部的成员变量列表
unsigned int count = 0;
Ivar *array = class_copyIvarList([tableView class], &count);
//遍历所有属性
for (int i = 0; i<count; i++) {
Ivar property = array[i];
const char *string = ivar_getName(property);
NSString *name = [[NSString alloc]initWithUTF8String:string];
//找出名字为@"_referencingCells"的属性,取出它的值
if (![name isEqualToString:@"_referencingCells"]) continue;
NSMutableArray * cells = object_getIvar(tableView, property);
int count = (int)cells.count;
if(!count) continue;
//设置字体颜色
UIColor *textColor = nil;
//设置label背景色
UIColor *labelBackgroundColor = nil;
//遍历UIPickerTableView 中的cell,并取出每个cell中的textLabel进行设置相关属性
for (int i = 0; i< count; i++) {
//设置其他部分的文字颜色、大小以及label背景等
if (index !=pickerTableViewNum -1) {
font = self.otherTextFont? self.otherTextFont:[UIFont systemFontOfSize:16];
textColor = self.otherTextColor ? self.otherTextColor : [UIColor grayColor];
labelBackgroundColor = self.otherLabelColor ?self.otherLabelColor : [UIColor clearColor];
}else{
font = self.selectedTextFont ? self.selectedTextFont : [UIFont systemFontOfSize:16];
textColor = self.selectedTextColor ? self.selectedTextColor : [UIColor blackColor];
labelBackgroundColor = self.selectedLabelColor ? self.selectedLabelColor : [UIColor clearColor];
}
UILabel *textLabel = [cells[i] subviews][1];
textLabel.textColor = textColor;
textLabel.font = font;
textLabel.backgroundColor = labelBackgroundColor;
if (index != pickerTableViewNum - 1)
continue;
if (textLabel.subviews.count>=1)
continue;
//dynamic seperator(设置动态分割线)
if (self.pickerViewType == PickerViewTypeDynamicSperator) {
UIView *line = [[UIView alloc]initWithFrame:CGRectMake((textLabel.frame.size.width - textWidth)/2, textLabel.frame.size.height - 1, textWidth, 1)];
line.backgroundColor = self.seperateLineColor;
[textLabel addSubview:line];
}
}
}
}
//static operate line(设置静态分割线)
if (self.pickerViewType != PickerViewTypeStaticSperator) return;
//通过多次测试,可以粗略计算出每列之间的间隔大概是4.75
CGFloat spacing = 4.75f;
NSInteger numberOfComponent = [self numberOfComponents];
CGFloat margin = (self.width - self.componentWidth * numberOfComponent - (numberOfComponent - 1)*spacing)/2;
CGFloat textLabelOffSet = 9.0f;
CGFloat textOffSet = (self.componentWidth - textLabelOffSet - textWidth)/2;
//取出`UIPickerColumnView`中的最后一个 `UIView`控件
UIView *view = [self.picker.subviews[0].subviews[component].subviews lastObject];
//UIView控件里面在修改前只有一个UIPickerTableView控件,我们需要添加静态分割线
if (view.subviews.count>=3) {//不能多加啊
}else{
CGFloat x = (spacing+self.componentWidth)*component + margin + textLabelOffSet+textOffSet;
UIView *lineView1 = [[UIView alloc]initWithFrame:CGRectMake(x, view.height - 1, textWidth, 1)];
UIView *lineView2 = [[UIView alloc]initWithFrame:CGRectMake(x, 0, textWidth, 1)];
lineView1.backgroundColor = [UIColor blueColor];
lineView2.backgroundColor = [UIColor blueColor];
[view addSubview:lineView1];
[view addSubview:lineView2];
}
上述代码基本是设置所有分割线部分的关键代码
3 关于日期的处理
这部分就不多做阐述了,主要是对每月天数的处理稍微复杂点,至于其他的基本好处理,我们只要知道1、3、5、7、8、10、12是31天,4、6、9、11是30天,2月份又分为平年和闰年,闰年29天,平年28天即可,许多demo种也有相关的计算,我觉得我代码中的书写应该也可以比较清晰的阐述,还有就是滑动的时的日期更新,我们需要注意一点儿小问题,如果想看内部实现的话欢迎大家看我的源码https://github.com/DreamOfXM/XMDatePicker.git
相关使用我已经在github上
如果发现有什么问题,一定要给我提出来哦,当然也欢迎交流相关技术问题