资讯专栏INFORMATION COLUMN

用UIPickerView简单定制一个UIDatePicker

zoomdong / 1412人阅读

摘要:从类的继承关系上来看继承自继承自而这两个类之间并无继承关系。事实上,,和这三个是差不多的设计。直接讲如何定制。实现的思想是,首先要确定给出的时间范围,所有展示的选项不能超过这个范围。

做应用的时候免不了会对某些UI控件做一些样式上的定制,比如Button的背景色,圆角,阴影等元素的调整。UIDatePicker也是一个比较常用的UI控件,iOS 7简约的设计风格在某些场景下可能并不是很合适,所以UIDatePicker有时也是一个有较大定制需求的控件。但是令人匪夷所思的一点是,尽管UIDatePickerUIPickerView看起来好像是差不多的两个UI组件,但是从iOS的API上来看,这两个组件的类之间并无继承关系。从类的继承关系上来看:

UIPickerView继承自UIView : UIResponder : NSObject

UIDatePicker继承自UIControl : UIView : UIResponder : NSObject

而这两个类之间并无继承关系。UIControl这个类是用来处理UI控制事件的,这意味着UIDatePicker可能对控制事件的处理更加精细,但平常的使用上我们可能感受不到与UIPickerView太多差别。而且要命的一点在于,UIDatePicker并不支持样式定制,这一点官方文档已经给出来了,连改个单元格的高度和栏目宽度都没戏。所以很遗憾,如果你要改变这玩意的样式,只能另辟蹊径,最方便的搞法就是找长得差不多的UIPickerView下手。如果你没有太复杂的需求的话,直接定制UIPickerView可能是一个最方便的选择。在我的案例里,暂时只有修改字号、字体,以及行高行宽的需求,所以用这个方法是在合适不过了。

修改UIPickerView的样式需要了解这个类相关的两个Protocol:UIPickerViewDataSourceUIPickerViewDelegate
如果对UITableView比较熟悉的话,看到这两个Protocol的名字应该能很快猜出接下来是个什么搞法。事实上,UITableViewUICollectionViewUIPickerView这三个是差不多的设计。最复杂的是UICollectionView,搞明白了这个,另外两个看一眼就知道怎么回事。这里我们不多分析这三个组件的共同点。直接讲如何定制。

在此我们需要创建一个新的类,并实现这两个Protocol中的一些方法:

UIPickerViewDataSource中有两个
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView //返回栏目数量,时间日期一般可以用三栏(年、月、日)
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component //每一栏的行数

UIPickerViewDelegate中有4个方法
-(CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component //用来修改行高
-(CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component //修改每一栏的宽度
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component //选中某栏目某行后的动作

后面三个方法挑一个实现就可以(UIPickerViewDelegate)
-(NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component //返回一个带属性的字符串(包含字体信息),如果只是修改字号,可以实现这个方法
-(UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view //返回一个UIView类实例,如果需要对每个cell的可重用视图进行定制,可以实现这个方法。
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component //如果没有字体定制需求,实现这个方法最简单。

这里最关键的方法是返回每一栏行数的方法以及最后返回跟title有关的方法。实现的思想是,首先要确定给出的时间范围,所有展示的选项不能超过这个范围。其次就是每一行应该对应显示哪一条。然后didSelectRow则是对应选择后的数据操作。具体的做法和UITableView差不多。下面给出一个参考实现,注意,涉及iOS时间操作的API本文不多加阐述,请读者自行查阅API文档:

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    switch (component) { // component是栏目index,从0开始,后面的row也一样是从0开始
        case 0: { // 第一栏为年,这里startDate和endDate为起始时间和截止时间,请自行指定
            NSDateComponents *startCpts = [self.calendar components:NSYearCalendarUnit
                                                           fromDate:self.startDate];
            NSDateComponents *endCpts = [self.calendar components:NSYearCalendarUnit
                                                           fromDate:self.endDate];
            return [endCpts year] - [startCpts year] + 1;
        }
        case 1: // 第二栏为月份
            return 12;
        case 2: { // 第三栏为对应月份的天数
            NSRange dayRange = [self.calendar rangeOfUnit:NSDayCalendarUnit
                                              inUnit:NSMonthCalendarUnit
                                             forDate:self.selectedDate];
            DLog(@"current month: %d, day number: %d", [[self.calendar components:NSMonthCalendarUnit fromDate:self.selectedDate] month], dayRange.length);
            return dayRange.length;
        }
        default:
            return 0;
    }
}

- (UIView *)pickerView:(UIPickerView *)pickerView
            viewForRow:(NSInteger)row
          forComponent:(NSInteger)component
           reusingView:(UIView *)view
{
    UILabel *dateLabel = (UILabel *)view;
    if (!dataLabel) {
        dataLabel = [[UILabel alloc] init];
        [dateLabel setFont:self.font];
        [dateLabel setTextColor:self.fontColor];
        [dateLabel setBackgroundColor:[UIColor clearColor]];
    }

    switch (component) {
        case 0: {
            NSDateComponents *components = [self.calendar components:NSYearCalendarUnit
                                                            fromDate:self.startDate];
            NSString *currentYear = [NSString stringWithFormat:@"%d", [components year] + row];
            [dateLabel setText:currentYear];
            dateLabel.textAlignment = NSTextAlignmentRight;
            break;
        }
        case 1: { // 返回月份可以用DateFormatter,这样可以支持本地化
            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
            formatter.locale = [NSLocale currentLocale];
            NSArray *monthSymbols = [formatter monthSymbols];
            [dateLabel setText:[monthSymbols objectAtIndex:row]];
            dateLabel.textAlignment = NSTextAlignmentCenter;
            break;
        }
        case 2: {
            NSRange dateRange = [self.calendar rangeOfUnit:NSDayCalendarUnit
                                                    inUnit:NSMonthCalendarUnit
                                                   forDate:self.selectedDate];
            NSString *currentDay = [NSString stringWithFormat:@"%02d", (row + 1) % (dateRange.length + 1)];
            [dateLabel setText:currentDay];
            dateLabel.textAlignment = NSTextAlignmentLeft;
            break;
        }
        default:
            break;
    }

    return dateLabel;
} 

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    NSInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
    switch (component) {
        case 0: {
            NSDateComponents *indicatorComponents = [self.calendar components:NSYearCalendarUnit
                                                                   fromDate:self.startDate];
            NSInteger year = [indicatorComponents year] + row;
            NSDateComponents *targetComponents = [self.calendar components:unitFlags
                                                                  fromDate:self.selectedDate];
            [targetComponents setYear:year];
            self.selectedDateComponets = targetComponents;
            [pickerView selectRow:0 inComponent:1 animated:YES];
            break;
        }
        case 1: {
            NSDateComponents *targetComponents = [self.calendar components:unitFlags
                                                                  fromDate:self.selectedDate];
            [targetComponents setMonth:row + 1];
            self.selectedDateComponets = targetComponents;
            [pickerView selectRow:0 inComponent:2 animated:YES];
            break;
        }
        case 2: {
            NSDateComponents *targetComponents = [self.calendar components:unitFlags
                                                                  fromDate:self.selectedDate];
            [targetComponents setDay:row + 1];
            self.selectedDateComponets = targetComponents;
            break;
        }
        default:
            break;
    }
    [pickerView reloadAllComponents]; // 注意,这一句不能掉,否则选择后每一栏的数据不会重载,其作用与UITableView中的reloadData相似
}

另外,如果希望对操控事件进行处理,我们创建的类可以继承UIControl,并实现sendAction:to:forEvent:方法。如果没有这个需求,直接继承NSObject就行了。

如果需要修改控件的背景材质,可以替换UIPickerView的index为2的subview。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/15601.html

相关文章

  • [分享]iOS开发-datePicker的详解

    摘要:日期选取器的各列会按照指定的风格进行自动配置,这样就让开发者不必关心如何配置表盘这样的底层操作。使用它只需要创建一个对象默认情况下选取会显示目前的日期和时间,并提供几个表盘,分别显示可以选择的月份和日期小时分钟以及上午下午。 UIDatePicker 是一个控制器类,封装了 UIPickerView,但是他是UIControl的子类,专门用于接受日期、时间和持续时长的输入。日期选取器的...

    aisuhua 评论0 收藏0
  • iOS开发③UIView

    摘要:用到的文本框的地方很多,比如搜索框用户登录框等。实现年活动指示器出于非活动状态时则会隐藏停止旋转开始旋转示例图步进器的作用是按照约定的步长进行增减操作。表示最小值,表示最大值,表示初始化时的值,表示步长。 UILabel Lable的作用是显示不可编辑的文字。 属性检查器 Text:Label显示的文字 Color:文字的颜色 Font:字体和字号 Alignment:文本的对齐方式...

    luck 评论0 收藏0
  • [分享]iOS开发-通过修改UIdatePicker的字体颜色来认清Runtime的真相

    摘要:找到苹果用于修改字体颜色的属性使用动态修改。在这个解决的过程中,我试过了遍历属性还有查看官方都找不到任何资料,甚至都想不通苹果内部是怎么处理的这么牛逼的。给设置参数,设置,并且动态调用设置参数苹果默认并且不开放其实也是的一种方式。 这篇文章对思路有极大的启迪性 本篇文章主要介绍了通过修改UIdatePicker的字体颜色来认清Runtime的真相,主要涉及到方面的内容,对于IOS开发感...

    Freelander 评论0 收藏0
  • iOS开发学习路线

    摘要:开发学习路线前言这里筑梦师是一名正在努力学习的开发工程师目前致力于全栈方向的学习希望可以和大家一起交流技术共同进步用简书记录下自己的学习历程个人学习方法分享本文阅读建议一定要辩证的看待本文本文主要是本人对开发经验中总结的知识点本文所有观点仅 iOS开发学习路线 前言 这里筑梦师,是一名正在努力学习的iOS开发工程师,目前致力于全栈方向的学习,希望可以和大家一起交流技术,共同进步,用简书...

    ctriptech 评论0 收藏0
  • Xcode-Snippets/Objective-C 学习

    摘要:醉了这两个也醉了,不过蛮实用断言调试宏,分别对应的异步等待嵌套。的一堆回调这顾名思义了不过是强迫症写法路径最实用,没有之一调用系统邮件短,然并卵跟直接输入有什么区别。协议实现相关方法短,然并卵,本地化字符串的回调 前言 Xcode-Snippets是github上的一堆开源代码。作者mattt分享了他的Xcode-Snippets(xcode代码片段),今天我们来学习一下。 片段 ...

    jayzou 评论0 收藏0

发表评论

0条评论

zoomdong

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<