自定义的iOS的滚轮选择器,附带农历选择器实现代码


http://www.cocoachina.com/bbs/read.php?tid=85374&page=1 


声明:
1.鉴于模拟器和真机的滚动效果的差异,建议大家真机调试的时候注释掉IDJScrollComponent.m里的如下内容:
self.decelerationRate=0;

2.我的代码使用了iOS5.0的UIImage的缩放图片的resizableImageWithCapInsets:方法,如果你使用的是iOS5.0以下的SDK,请替换为stretchableImageWithLeftCapWidth:topCapHeight:方法。

3.几处BUG:
(1.)copy的我忘记释放内存了。
NSString *pYear=[cal.year copy];
NSString *pMonth=[cal.month copy];
这个pYear, pMonth没释放!

(2.)UITableViewCell的释放有些问题
cellForRowAtIndexPath方法里面, cell的初始化有以下三种情况, 
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];

假设是调用这句 cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 初始化
然后最后return [cell autorelease]; 就有问题了!

这样改, 把另外2种初始化改成
cell=[[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell=[[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
然后最后return cell;就OK了。

(3.)算法的局限性。
IDJScrollComponent.m里的moveSubViews:方法由于算法欠妥,导致了一个局限性,就是滚轮图片的高度必须是可以被显示的行数整除的数字,譬如:我的例子日期显示三行,那么滚轮的图片高度就是177像素的,177恰好可以被3整除。

(4.)农历数据提供器IDJChineseCalendar.mm的- (id)initWithYearStart:(NSUInteger)start end:(NSUInteger)end:方法:
//获取当年的公历年份对应的农历的年代和甲子年份
        std::pair p=ChineseCalendarDB::GetEraAndYearOfLunar([self.year intValue]);
需要移动到
//设置当前时间的农历日期字段
        self.year=[NSString stringWithFormat:@"%d", chineseDate.GetYear()];
的后面。


这个控件并不是对iOS自带的UIPickerView贴图,而是重新实现,我一周多的成果,期间走了不少弯路。哈!

自定义的iOS的滚轮选择器,附带农历选择器实现代码_第1张图片
 
此控件开发的原因:
最近有个需求,需要做一个农历的日期滚轮选择器,一开始想用iOS自带的UIPickerView,但是设计人员要求的比较高,必须实现她要求的样式,也就是上图中大家看到的样式。但是UIPickerView根本没有提供可以定制样式(也就是更换皮肤)的接口。首先我在论坛上找到了帖子,按照这位兄台所述,确实可以在一定程度上修改UIPickerView的图片,我之所以说一定程度上,有以下几个原因:
(1.)他是用覆盖原有的UIPickerView上的各个子视图上图片的方法做的,那么你提供新的图片就必须与原图片严丝合缝。这也说明,即便是图片换了,样式也是不能换的,譬如:分割线的粗细、上下左右的边框的大小等,也就是不能完全DIY,不能满足我的需求。
(2.)由于覆盖UIPickerView上的各个子视图上图片的做法不是苹果官方给出的,一旦以后某个版本的UIPickerView实现策略改变,例如:3号子视图不再是滚轮的背景图片,你的做法就挂了。

于是我就实现了上图中的滚轮选择器,它与UIPickerView的区别是:
(1.)皮肤图片提供了方法可以做替换的。
(2.)可以选择滚轮上的数据是否循环滚动。
(3.)可以指定选择条的位置。


自定义的iOS的滚轮选择器,附带农历选择器实现代码_第2张图片
 
为了代码的复用,也就是所谓的OOP,我按照以下的方式实现(因为我觉得这个滚轮选择器的实现方案其实算不上亮点,因为有很多的方案可以选择的,但是程序的结构、代码的重用是很重要的):
(1.)pickerview目录:这个目录中的IDJPickerView.h是滚轮选择器视图,没有任何和与数据相关的东西,也就是你可以在IDJPickerView上显示任何数据。IDJPickerView通过委托获取需要显示的数据。这个目录下的IDJScrollComponent.h实现了UIScrollView上的内容可以反复循环滚动的功能。
(2.)datepicker目录:IDJDatePickerView.h实现了IDJPickerView里的协议,并把IDJPickerView做为自己的一个私有成员,实现了一个可以根据type字段显示公历、农历的日期选择器,IDJDatePickerView.m里的农历算法用的是solar_chinese_calendar目录中的C++算法,因此农历的相关数据类使用了C++混编的代码。其实iOS自己支持农历的,但是存在BUG,具体的原因大家可以看我的IDJChineseCalendar.mm的源码,里面的注释比较详细的。我在这里也使用了一层封装,也就是IDJDatePickerView.m本身也不提供数据,而是通过IDJCalendar.h、IDJChineseCalendar.h、IDJCalendarUtil.h来提供,这样既实现了数据与视图的分离(因为农历算法太复杂了,直接写在IDJDatePickerView.m里会使得代码看起来可读性太差了),而且把C++的调用封装在了数据层,使得IDJDatePickerView.m的视图层代码不会出现C++的API。
(3.)timepicker目录:这个目录纯粹是一个Demo,展示了一个不循环滚动、显示行数与选中条位置不对称的选择器,没有什么实际的含义。

一开始我是纯粹写农历的选择器,代码都是耦合在一起的,后来逐个拆开的,这样代码就可以复用了,不仅仅是农历选择器,而是可以承载任何数据。



下载demo:点击打开demo链接,下载







你可能感兴趣的:(ios)