XMDatePicker写作思路

作者: 郭苒 | 来源:发表于2017-03-10 12:21 被阅读197次

    先上一张效果图(原谅我还是这么无聊~~,我觉得动态的可能更能展示作品的特性)

    0.gif
    写这个时间选择器之前我还没写过相关的东西,没写过其实是瞎话,以前在项目中用过UIDatePicker,所以可自定义性不是很强,也没啥可说的,虽然在我们的项目中时间选择器很常见,但是认真研究的人就不知道了……,这次我也算是认真的看了一下UIPickerView内部的组成。先说说内部的组成吧。

    1 UIPickerView内部组成

    在xcode的可视化工具中你可以看到UIPikerView的结构如下:

    1.png
    当然如果,你也可以打印一下UIPikerView的子空间,会发现,它有三个子空间,其中两个是一样的,而且高度是0.67,所以我们可以猜测这个高度是0.67的view就是那两条灰色的分割线。不信你可以试试,简要做以下测试
     self.picker.subviews[1].hidden = YES;
     self.picker.subviews[2].hidden = YES;
    

    此时,你会惊奇的发现两条灰色的分割线不见了,那么证实了我们猜测的是正确的,那么接下来往下走吧。再来看结构,如果说你嫌看结构图麻烦的话,你可以利用打印子控件的方式看查看其内部的控件,我是采用后者的,不过这里为了更加形象,我采用图形结合的方式来讲。如下图:


    2.png

    请原谅我的懒惰,这里我把结构图全部展开来讲吧,不再一张一张的贴图了,

    • UIPickerView的suviews 展开这个后我们看到里面又有3个UIPickerColumnView,当然你可以利用日志打印一下,会发现前三个是完全一样的,我们可以猜测每一个代表着一列,不信你可以改变数据源试试,会发现猜测的正确性。
    • 接下来我们进入到每一列UIPickerColumnView,发现有三个UIView(子控件),前两个完全一样,第三个不同,我猜测,第三个是中间行,也就是选择到日期的那一行
    • 以此类推,你可以看到中间行里面有一个子控件,名字是UIPickerTableView,这是苹果内部私有类,在UIKit框架中我们找不到它的头文件,但是依旧不影响我们获取到它。
      快上代码,不多啰嗦了

    2 为时间选择器自定义分割线

    我们可以拿到UIPickerTableView然后利用runtime机制取出其内部的属性,如果你有印象的话,我说过每个UIPickerColumnView中有三个UIView控件,每个UIView控件中只有一个子控件UIPickerTableView,而三个UIView控件中的第3个才是我们选择时间的那一行。

    上述话的意思简单翻译为UIPickerColumnViewUIView控件数 = 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
    如果发现有什么问题,一定要给我提出来哦,当然也欢迎交流相关技术问题

    相关文章

      网友评论

      • long弟弟:设置fromYear程序会崩溃,抽时间解决下吧,感谢!
        郭苒:相关代码已更新,如有问题请及时告知,谢谢
        郭苒:不好意思啊,最近忙,没及时回复,这两个属性已经过期了,没及时注销,抱歉啊,现在都是maximumDate和minimumDate
      • iaiayao:看了代码后发现没有设置最大日期和最小日期的限制,这个可以完善一下
        iaiayao:@郭苒 :+1:
        郭苒:@melody_yoo 你说的最大时间和最小时间已经更新,另外将整个代码框架都进行了较大的调整,请知悉
        郭苒:@melody_yoo 谢谢,本想找时间重构一下代码的,哈哈,回家就很懒了……我会尽快完善
      • IT:self.picker.subviews[1].hidden = YES;
        self.picker.subviews[2].hidden = YES;
        会奔溃啊
        郭苒:单独隐藏这两条线是不会崩溃的,可参看我的源码里- (void)p_setSelectedRowTitleLabelOfComponent:(NSInteger)component这个方法,至于说你的崩溃,我没看你是怎么写的,所以也不好说
      • Vincent20481:直接打赏了
        郭苒:谢谢!

      本文标题:XMDatePicker写作思路

      本文链接:https://www.haomeiwen.com/subject/crengttx.html