




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】(ios实现)用cc++混合编程方式为iosandroid实现一个自绘日期选择控件(二)
二、IOS实现版本:1、程序结构:千言万语,不如一张图来的清晰1)
由于CalendarController包含了一个UITableView指针,因此CalendarController需要实现UITableDataSource以及UITableViewDelegate与UITableView进行交互。
2)
UITableView包含多个CalendarView,这样就能利用UITableView的手势滑动功能以及Cell重用功能。
3)
CalendarView继承自UIControl,因为UIControl将相对底层的触摸事件转换为容易操作的控件事件。主要为了使用UIControlEventTouchUpInside这个事件。
4)
CalendarDelegate仿照ios
mvc模式,用于类之间的解耦(面向接口编程)以及类之间的通信。2、CalendarDelegate协议:@protocol
CalendarDelegate
<NSObject>
//年月和UITableView以及其中CalendarView之间关系映射
//具体见下面代码分析
-(int)
calcCalendarCount;
-(SDate)
mapIndexToYearMonth
:
(int)
index;
-(int)
mapYearMonthToIndex
:
(SDate)
date;
//用于显示到指定的年月范围
-(void)
showCalendarAtYearMonth
:
(SDate)
date;
//用于时间期限管理以及选中判断
-(BOOL)
isInSelectedDateRange
:
(SDate)
date;
-(void)
setSelectedDateRangeStart
:
(SDate)
start
end
:
(SDate)
end;
-(void)
setEndSelectedDate
:
(SDate)
end;
//迫使整个UITableView重绘
-(void)
repaintCalendarViews;
//计数器,用于判断touch次数
-(void)
updateHitCounter;
-(int)
getHitCounter;
@end3、CalendarController://.h文件接口声明
#import
<UIKit/UIKit.h>
@interface
ViewController
:UIViewController
@end//.m文件
#import
"ViewController.h"
#import
"CalendarDelegate.h"
#import
"CalendarView.h"
//实现了如下三个delegate
@interface
ViewController
()
<UITableViewDataSource,UITableViewDelegate,CalendarDelegate>
{
//用于计算出多少个月历,具体见下面代码
int
_startYear;
int
_endYear;
//每个月历控件的高度,上面的个数和此地的高度,就可以计算整个UITableView的高度以及进行定位操作
float
_calendarHeight;
//用于选中操作时候,时间范围的比较(time_t实际是个64位的整型值,适合做比较操作,具体看实现代码)
time_t
_startTime;
time_t
_endTime;
}
//选中值的年月表示方式,方便显示而已,实际操作都转换成time_t类型
@property
(nonatomic,assign)
SDate
begDate;
@property
(nonatomic,assign)
SDate
endDate;
//点击计数器,用于确定当前点击的奇偶性,因此改月历控件涉及两次操作,用于区域选者
@property
(nonatomic)
int
hitCounter;
//作为Calendar的父容器,用于处理滑动以及cell重用
@property
(weak,
nonatomic)
IBOutlet
UITableView
*tableView;
@end-(int)
calcCalendarCount
{
SDate
date;
date_get_now(&date);
//计算出当前的年月到n年前的1月份的月数
//加设当前为2016年8月,n为5,则月份范围为[2011年1月---2016年8月
总计月数为68],具体算法如下:
int
diff
=
_endYear
-
_startYear
+
1;
diff
=
diff
*
12;
diff
-=
12
-
date.month;
return
diff;
}
//UITableView的DatatSource有个必须实现的协议函数,用于返回当前UITableView可以容纳的总数:
-
(NSInteger)tableView:(UITableView
*)tableView
numberOfRowsInSection:(NSInteger)section
{
int
ret
=
[self
calcCalendarCount];
return
ret;
}-
(CGFloat)tableView:(UITableView*)tableView
heightForRowAtIndexPath:(NSIndexPath
*)indexPath
{
//返回的是当前的calendarView的高度
//UITableView需要知道月历(月份)的个数以及月历控件的高度,就可以计算出整个UITableView的Content的height了
return
_calendarHeight;
}千言万语,不如再来一张图来的清晰-(SDate)
mapIndexToYearMonth
:
(int)
index
{
SDate
ret;
//调用c函数,将索引号映射成年月,用于UITableView创建calendarView时现实月历标题
date_map_index_to_year_month(&ret,
_startYear,
index);
return
ret;
}
//调用mapIndexToYearMonth:
-
(UITableViewCell
*)tableView:(UITableView
*)tableView
cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static
NSString*
calendarID
=
@"calendarID";
float
width
=
self.tableView.frame.size.width;
//从行索引号映射到年月
SDate
date
=
[self
mapIndexToYearMonth:(int)indexPath.row];
//获取重用的cell
UITableViewCell
*cell
=
[tableView
dequeueReusableCellWithIdentifier:calendarID
];
//如果为null,说明不存在,创建该cell
if(cell
==
nil)
{
//可以在此断点,查看一下具体生成了多少个calendarView(我这里生成了3个)
//说明UITableView可见rect有三个calendarView相交
cell
=
[[UITableViewCell
alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:calendarID];
[cell
setTag:10];
//手动创建CalendarView
CalendarView*
calendarView
=
[[CalendarView
alloc]
initWithFrame:CGRectMake(0,
0,
width,
_calendarHeight)];
//设置CalednarDelegate
calendarView.calendarDelegate
=
self;
//给定一个tag号,用于重用时获得该view
[calendarView
setTag:1000];
[cell.contentView
addSubview:calendarView];
}
//通过tag号,获取view
CalendarView*
view
=(CalendarView*)
[cell.contentView
viewWithTag:1000];
//设置CalendarView的年月
[view
setYearMonth:date.year
month:date.month];
//[view
setNeedsDisplay];
return
cell;
}
-(int)
mapYearMonthToIndex:(SDate)date
{
int
yearDiff
=
date.year
-
_startYear;
int
index
=
yearDiff
*
12;
index
+=
date.month;
index
-=
1;
return
index;
}
//调用mapYearMonthToIndex
-(void)
showCalendarAtYearMonth:(SDate)date
{
if(date.year
<
_startYear
||
date.year
>
_endYear)
return;
//将年月表示映射成UITableView中的索引号,根据索引计算出要滚动到的目的地
int
idx
=
[self
mapYearMonthToIndex:date];
//如上图所示:当idx
=
calendarViews.length-1时,可能存在超过整个UITableView
ContentSize.height情况,此时,UITableView会自动调整contentOffset的值,使其符合定位到最底端,android
listview也是如此。
self.tableView.contentOffset
=
CGPointMake(0.0F,
idx
*
_calendarHeight
);
}
1)
从上图以及代码,应该很清楚的了解了映射和定位问题的过程
2)
从上图中,我们也可以了解到UITableView的滚动原理,UITableView的Frame是Clip区域,滚动的内容存放于Content中。
3)
UITableView可以说是移动开发中最常用,最重要的一个控件(还有一个是UICollectionView)。有两个主要功能点:滚动(UIScrollView父类)和cell复用。以后有机会我们来从头到尾实现一个带有上述功能的控件。-
(void)viewDidLoad
{
[super
viewDidLoad];
//
Do
any
additional
setup
after
loading
the
view,
typically
from
a
nib.
//获取当前的年月
SDate
date;
date_get_now(&date);
//default有3年
_startYear
=
date.year-3;
_endYear
=
date.year;
/*
//当年也支持
_startYear
=
date.year;
_endYear
=
date.year;
*/
//touch
计数器
_hitCounter
=
0;
float
scale
=
0.6F;//硬编码,最好由外部设置
//float
scale
=
0.5F;//硬编码,最好由外部设置
_calendarHeight
=
self.tableView.frame.size.height
*
scale;
self.tableView.dataSource
=
self;
self.tableView.delegate
=
self;
//default定位显示当前月份
if
(self.begDate.year
==
0)
{
self.begDate
=
date;
[self
showCalendarAtYearMonth:date];
}else{
//当然你也可以设置具体月份重点显示
[self
showCalendarAtYearMonth:self.begDate];
}
}到目前为止,支持CalendarController运行的所有方法都分析完毕,接下来我们要看一下CalendarView相关的实现。(CalendarDelegate还有一些方法没分析,因为这些方法是由CalendarView调用的,由此可见,IOS中的Delegate除了面向接口编程外,还有一个功能就是类之间的通信)4、CalendarView://.h文件
@interface
CalendarView
:
UIControl
-(void)
setYearMonth
:
(int)
year
month
:
(int)
month;
@property
(weak,
nonatomic)
id
calendarDelegate;
@end//.m文件
@interface
CalendarView()
{
/*
blf:
引用c结构,所有月历相关操作委托给SCalendar的相关函数
SCalendar
使用栈内存分配
*/
SCalendar
_calendar;
//这是一个很重要的变量,具体源码中说明
int
_lastMonthDayCount;
//存放月历的日期和星期字符串
NSMutableArray*
_dayAndWeekStringArray;
//string绘制时的大小
CGSize
_dayStringDrawingSize;
CGSize
_weekStringDrawingSize;
}-
(id)initWithFrame:(CGRect)frame
{
self
=
[super
initWithFrame:frame];
if
(self)
{
//
Initialization
code
//年月区块和星期区块的大小按当前view高度的比例来设定
float
yearMonthHeight
=
frame.size.height
*
0.095F;
float
weekHeight
=
frame.size.height
*
0.089F;
//初始化月历控件,计算出各个区块部分的大小
calendar_init(&_calendar,
frame.size,
yearMonthHeight,
weekHeight);
SDate
date
=
_calendar.date;
//此时date是上个月
date_get_prev_month(&date,
1);
self.backgroundColor
=
[UIColor
clearColor];
//设置日期区块的大小
CGRect
rc;
calendar_get_day_cell_rect(&_calendar,&rc,0,0);
CGSize
size;
size.height
=
rc.size.height-
15
;
size.width
=
rc.size.width
-
15;
//预先分配38个字符串容量的数组
_dayAndWeekStringArray
=
[NSMutableArray
arrayWithCapacity:38];
//0--30表示最多31天日期字符串
for(int
i
=
0;
i
<
31;
i++)
[_dayAndWeekStringArray
addObject:
[NSString
stringWithFormat:@"%02d",i+1]];
//31--37存储星期字符串
[_dayAndWeekStringArray
addObject:@"周日"];
[_dayAndWeekStringArray
addObject:@"周一"];
[_dayAndWeekStringArray
addObject:@"周二"];
[_dayAndWeekStringArray
addObject:@"周三"];
[_dayAndWeekStringArray
addObject:@"周四"];
[_dayAndWeekStringArray
addObject:@"周五"];
[_dayAndWeekStringArray
addObject:@"周六"];
//计算出日期字符串的绘制用尺寸
_dayStringDrawingSize
=
[self
getStringDrawingSize:
[_dayAndWeekStringArray
objectAtIndex:0]];
//计算出星期字符串的绘制用尺寸
_weekStringDrawingSize
=
[self
getStringDrawingSize:
[_dayAndWeekStringArray
objectAtIndex:31]];
//UIControl基于控件的事件处理系统,挂接UIControlEventTouchUpInside处理程序
[self
addTarget:self
action:@selector(handleTouchEvent:forEvent:)
forControlEvents:UIControlEventTouchUpInside];
}
return
self;
}
//计算要绘制字符串的尺寸的函数如下:
-(CGSize)
getStringDrawingSize:(NSString*)str
{
NSAttributedString*
attStr
=
[[NSAttributedString
alloc]
initWithString:str];
NSRange
range
=
NSMakeRange(0,
attStr.length);
NSDictionary*
dic
=
[attStr
attributesAtIndex:0
effectiveRange:&range];
CGRect
rect
=
[str
boundingRectWithSize:CGSizeMake(0,
0)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:dic
context:nil];
return
rect.size;
}从上面类声明和初始化代码,引出几个问题:1)为什么继承自UIControl?
2)为什么delegate使用weak?
3)为什么delegate
声明为id?
4)为什么栈分配?
5)为什么同一个CalendarView的类声明需要分别在.h和.m文件中,或者换种说法:这样做有什么好处?
6)为什么初始化只实现了initWithFrame,没有实现initWithCoder,在哪种情况下,还需要override
initWithCoder函数?-(void)
drawStringInRectWithSize
:
(NSString*)
string
rect:(CGRect)rect
size:(CGSize)
size
color
:
(UIColor*)
color
{
CGPoint
pos;
//下面算法是让文字位于要绘制的Rect的水平和垂直中心
//也就是剧中对齐
pos.x
=
(rect.size.width
-
size.width)
*
0.5F;
pos.y
=
(rect.size.height
-
size.height)
*
0.5F;
pos.x
+=
rect.origin.x;
pos.y
+=
rect.origin.y;
//由于周日和周六与平常文字颜色有差别,因此需要color
NSDictionary
*
attsDict
=
[NSDictionary
dictionaryWithObjectsAndKeys:
color,
NSForegroundColorAttributeName,
nil
];
[string
drawAtPoint:pos
withAttributes:attsDict];
}
1)
opengles
API
利用gpu加速,速度最快,难度相对最大,自由度也最高,需要创建专用的GL上下文环境。基于状态机模式,需要设置各种绘制状态以及恢复状态。最重要的是跨平台,android以及windows,Linux都可以用(cocos2d-x基于opengles)。
2)
quartz
API
使用cpu光栅化,不需要GL上下文环境,直接可在控件表面进行绘制,相对底层,基于状态机模式,需要设置各种绘制状态以及恢复状态
3)
UIKit中对quartz
API的二次封装,例如UIBezierPath类,封装了大部分的shape,方便易用,我们就用这个类来进行绘制。上面两种API,以后有机会我们可以专门来分析一下。圆的贝塞尔路径对象(由圆心和半径定义):-(void)
drawCircleInRect
:
(CGRect)
rect
color
:
(UIColor*)
color
isFill
:
(BOOL)
isFill
{
//取width和height最小的值作为要绘制的圆的直径,这样就不会将圆绘制范围超出rect
float
radiu
=
rect.size.width
<
rect.size.height
?
rect.size.width
:
rect.size.height;
//将圆的中心点从rect的左上角平移到rect的中心点
CGPoint
center;
center.x
=
rect.origin.x
+
rect.size.width
*
0.5F;
center.y
=
rect.origin.
y
+
rect.size.height
*
0.5F;
//圆是由圆心和半径定义的
radiu
*=
0.5F;
//创建一个圆的bezier路径对象
UIBezierPath*
circle
=
[UIBezierPath
bezierPathWithArcCenter:center
radius:radiu
startAngle:0.0F
endAngle:2.0F*3.1415926F
clockwise:true];
//填充绘制(日期选中状态)
if(isFill
==
YES)
{
[color
setFill];
[circle
fill];
}
else
{
//没选中状态,用stroke方式绘制
[color
setStroke];
[circle
stroke];
}
}圆角矩形的贝塞尔对象(由Rect和半径定义):-(void)
drawRoundRect
:
(CGRect)
rect
radius
:
(CGFloat)radius
{
UIBezierPath*
roundRect
=
[UIBezierPath
bezierPathWithRoundedRect:rect
cornerRadius:radius];
[[UIColor
colorWithRed:52/255.0
green:175/255.0
blue:248/255.0
alpha:1.0]
setFill];
[roundRect
fill];
}代码很长,我们拆分成几个区块来分析//1、见上图,绘制年月信息
//blf:获取原生绘图context指针,所有原生绘图api都是c语言api方式
//CGContextRef
context
=
UIGraphicsGetCurrentContext();
CGRect
rc;
calendar_get_year_month_section_rect(&_calendar,
&rc);
//NSString*
drawStr
=
@"
"
+
_calendar.date.year
+
@"年"
+
_calendar.date.month
+
@"月";
NSString*
drawStr
=
[NSString
stringWithFormat:@"%d年%d月",_calendar.date.year,_calendar.date.month];
//绘制年月信息
[self
drawYearMonthStr:drawStr
rect:rc];//2、见上图,绘制星期信息
//_dayAndWeekStringArray中31-37索引保存的是星期字符串
for(int
i=
0;
i
<
7;
i++)
{
//获取星期区块中某个cell的rect
calendar_get_week_cell_rect(&_calendar,
&rc,
i);
if(i
==
0
||
i
==
6)
{
//双休日黑色
[self
drawStringInRectWithSize:[_dayAndWeekStringArray
objectAtIndex:31
+
i]
rect:rc
size:_weekStringDrawingSize
color:
[UIColor
blackColor]];
}
else
{
//其他时间蓝色
[self
drawStringInRectWithSize:[_dayAndWeekStringArray
objectAtIndex:31
+
i]
rect:rc
size:_weekStringDrawingSize
color:
[UIColor
blueColor]];
}
}//3、见上图红色边框部分,绘制上个月日期信息
CGPoint
dayRectOffset;
//获取日期区块的rect
calendar_get_day_section_rect(&_calendar,
&rc);
//纪录日期区块的起始位置
dayRectOffset
=
rc.origin;
//当前月份1号在日期cells中的起始索引号
int
begin
=
_calendar.dayBeginIdx;
//当前月份结束索引号
int
end
=
begin
+
_calendar.dayCount;
//绘制上个月的日期,假设begin
=
5
i=[4,3,2,1,0]
for(int
i
=
begin
-
1;
i
>=
0;
i--)
{
calendar_get_day_cell_rect_by_index(&_calendar,
&rc,
i);
//计算出位置偏移量
rc.origin.x
+=
dayRectOffset.x;
rc.origin.y
+=
dayRectOffset.y;
//缩小一下绘制rect的尺寸而已
rc.origin.x
+=
5;
rc.origin.y
+=
5;
rc.size.width
-=
10;
rc.size.height
-=
10;
//绘制圆圈
[self
drawCircleInRect:rc
color:[UIColor
colorWithRed:245/255.0
green:245/255.0
blue:245/255.0
alpha:1.0]
isFill:YES];
//计算方式涉及到了_lastMonthDayCount
//假设上个月有30天,本月的begin为5,则
//则30-(5-4)=
29
>0base>30号
//
30-
(5-3)=
28
>0base>29号
//
30-
(5-2)=
27
>0base>28号
//
30-
(5-1)=
26
>0base>27号
//
30-
(5-0)=
25
>0base>26号
int
dayIdx
=
_lastMonthDayCount
-
(begin
-
i);
//绘制圆圈中的日期
[self
drawStringInRectWithSize:[_dayAndWeekStringArray
objectAtIndex:
dayIdx]
rect:rc
size:_dayStringDrawingSize
color:[UIColor
colorWithRed:223/255.0
green:223/255.0
blue:223/255.0
alpha:1.0]];
}//4、见上图红色边框部分,绘制下个月日期信息
for(int
i
=
end;
i
<
42;
i++)
{
calendar_get_day_cell_rect_by_index(&_calendar,
&rc,
i);
rc.origin.x
+=
dayRectOffset.x;
rc.origin.y
+=
dayRectOffset.y;
rc.origin.x
+=
5;
rc.origin.y
+=
5;
rc.size.width
-=
10;
rc.size.height
-=
10;
[self
drawCircleInRect:rc
color:[UIColor
colorWithRed:245/255.0
green:245/255.0
blue:245/255.0
alpha:1.0]
isFill:YES];
//索引是i-end,很容易理解的
[self
drawStringInRectWithSize:[_dayAndWeekStringArray
objectAtIndex:
i
-
end]
rect:rc
size:_dayStringDrawingSize
color:[UIColor
colorWithRed:223/255.0
green:223/255.0
blue:223/255.0
alpha:1.0]];
}typedef
struct
_selectRange
{
int
rowIdx;
//为了方便处理是否同一行
int
columIdx;//行列转换一纬数组索引
CGRect
rect;
//纪录要绘制的rect
}
selectRange;//5、绘制当前月份的日期,包括选中,未选中以及日期文字
//使用c结构,并初始化相关变量
selectRange
ranges[31];
memset(ranges,0,sizeof(ranges));
int
rangeCount
=
0;
//绘制当前月的日期
for(int
i
=
begin
;
i
<
end;
i++)
{
calendar_get_day_cell_rect_by_index(&_calendar,
&rc,
i);
rc.origin.x
+=
dayRectOffset.x;
rc.origin.y
+=
dayRectOffset.y;
rc.origin.x
+=
5;
rc.origin.y
+=
5;
rc.size.width
-=
10;
rc.size.height
-=
10;
SDate
date;
date_set(&date,
_calendar.date.year,
_calendar.date.month,
i
-
begin
+
1
);
//如果当前日期在选中时间范围内,则batch起来,由drawSelectRange进行绘制
//因为需要处理换行这种效果(drawSelectRange中处理,因此缓存起来二次处理比较方便)
//与delegate通信
if([self.calendarDelegate
isInSelectedDateRange:date])
{
ranges[rangeCount].rowIdx
=
i
/
7;
//映射成行索引
ranges[rangeCount].columIdx
=
i
%
7;
//映射成列索引
ranges[rangeCount].rect
=
rc;
//当前行列的rect纪录下来
rangeCount++;
//计数器增加1
}
else
{
//没有选中的,就直接绘制圆圈和当中的日期号
[self
drawCircleInRect:rc
color:[UIColor
colorWithRed:234/255.0
green:234/255.0
blue:234/255.0
alpha:1.0]
isFill:NO];
[self
drawStringInRectWithSize:[_dayAndWeekStringArray
objectAtIndex:
i
-
_calendar.dayBeginIdx]
rect:rc
size:_dayStringDrawingSize
color:[UIColor
colorWithRed:107/255.0
green:107/255.0
blue:107/255.0
alpha:1.0]];
}
}
//NSLog(@"select
day
count
=
%d",rangeCount);
//rangeCount纪录了选中的数量,ranges则纪录了要绘制的所有信息
[self
drawSelectRange:ranges
count:rangeCount];
//选中的圈圈的文字由下面代码绘制
for(int
i
=
0;
i
<
rangeCount;
i++)
{
//重新将行列(二维)索引号映射一纬数组索引号
int
idx
=
ranges[i].rowIdx
*
7
+
ranges[i].columIdx;
//idx
-
begin就是当前的要绘制的日期文字的索引号
[self
drawStringInRectWithSize:[_dayAndWeekStringArray
objectAtIndex:
idx
-
begin]
rect:ranges[i].rect
size:_dayStringDrawingSize
color:[UIColor
whiteColor]];
}//blf:注意
参数ranges是数组名,数组名表示数组的首地址
//
还有就是selectRange是c结构,当做指针操作时要用->而不是.寻址操作符
-(void)
drawSelectRange
:
(selectRange*
)
ranges
count
:
(int)
count
{
//两种情况下count
=
1
//第一选则,或者第二次选中的和第一次选中的是同一个日期cell
//此时是绘制圆形而不是roundedRect
if(count
==
1)
{
[self
drawCircleInRect
:
ranges[0].rect
color:[UIColor
colorWithRed:52/255.0
green:175/255.0
blue:248/255.0
alpha:1.0]
isFill:YES];
//退出函数
return;
}
//并不是第一次选者且第二次选者不是和第一次选者一致时
//获取cell
rect的width
CGRect
rect;
calendar_get_day_cell_rect_by_index(&_calendar,
&rect,
0);
float
width
=
rect.size.width;
//用于纪录上一次的行号,初始化,纪录的是第一行的索引号
int
lastRowIdx
=
ranges[0].rowIdx;
//计数器,用来纪录当前行的cell的数量
int
sameRowCellCount
=
0;
for(int
i
=
0;
i
<
count;
i++)
{
//从ranges数组中获取一个结构时候,使用了&取地址操作符
//因为防止发生拷贝,如果不是取地址的话,赋值会发生memcopy行为
selectRange*
range
=
&ranges[i];
//行号相同,则同一行啦
if(range->rowIdx
==
lastRowIdx)
{
sameRowCellCount++;
}
else
{
//行号不同,说明换行了,因此要绘制当前行
CGRect
rc;
//i
-
sameRowCellCount找到起始索引
rc.origin
=
ranges[i
-
sameRowCellCount].rect.origin;
rc.size.height
=
range->rect.size.height;
rc.size.width
=
width*
(sameRowCellCount)
-
10.0F;
//很可能存在这种情况,既选中的是周六开始的,因此绘制的是圆形而不是roundedRect
if(sameRowCellCount
==
1)
{
[self
drawCircleInRect:rc
color:[UIColor
colorWithRed:52/255.0
green:175/255.0
blue:248/255.0
alpha:1.0]
isFill:YES];
}
else
{
//一般情况,绘制roundedRect
[self
drawRoundRect:rc
radius:rc.size.height];
}
sameRowCellCount
=
1;//标记值,为了下面绘制最后一行的代码使用,=1和>1要分别处理
//纪录上一次的行号
lastRowIdx
=
range->rowIdx;
}
}
//将最后一行拆分出来单独处理,这样就方便处理一些特殊情况
//绘制最后一行
if(sameRowCellCount
>
0)
{
CGRect
rc;
rc.origin
=
ranges[count
-
sameRowCellCount].rect.origin;
rc.size.height
=
ranges[count
-
sameRowCellCount].rect.size.height;
rc.size.width
=
width*
(sameRowCellCount)
-
10.0F;
//最后一行有多个cell被选中
if(sameRowCellCount
!=
1)
{
[self
drawRoundRect:rc
radius:rc.size.height];
}
else//最后一行仅周日被选中,只有一个,圆圈
[self
drawCircleInRect:rc
color:[UIColor
colorWithRed:52/255.0
green:175/255.0
blue:248/255.0
alpha:1.0]
isFill:YES];
}
}//由于UITableView采用了cell重用机制,因此仅有很屏幕rect相交的cell存在
//所以是cells一直轮替交换,所以我们必须在每次自绘时候判断当前的cell中的月历的每个日期是否处于选中状态
//而本函数就是起到这样的作用,判断月历中某个日期是否处于选中的区间范围
-(BOOL)isInSelectedDateRange
:
(SDate)
date
{
time_t
curr
=
date_get_time_t(&date);
if(curr
<
_startTime
||
curr
>
_endTime)
return
NO;
return
YES;
}
控件的状态初始化
控件的绘制
控件的事件触发和处理
控件的布局-(void)
handleTouchEvent:(id)
sender
forEvent:(UIEvent
*)event
{
NSSet
*touches
=
[event
allTouches];
UITouch
*touch
=
[touches
anyObject];
//获取UITouch,将其转换到当前CalendarView的局部坐标系表示
CGPoint
upLoc
=
[touch
locationInView:self];
//通过局部坐标系的点获取点击处的cell的索引号,优化部分请看c的相关实现
//这个碰撞检测原理实际在游戏中经常使用,分区缩小范围,然后检测该范围内所有物体的与点(2D)
//或光线(3D)是否发生碰撞,用于此处也非常适合
int
hitIdx
=
calendar_get_hitted_day_cell_index(&_calendar,
upLoc);
//选中了,则
if(hitIdx
!=
-1)
{
SDate
date;
date_set(&date,
_calendar.date.year,
_calendar.date.month,
hitIdx
-
_calendar.dayBeginIdx
+
1);
//=0为第一次点击,仅选中一个cell
//mod为了周而复始,并在[0,1]之间
if([self.calendarDelegate
getHitCounter]
%
2
==
0)
{
//第一次点击,让开始和结束Date相同
[self.calendarDelegate
setSelectedDateRangeStart:date
end:date];
}
else//=1为第二次点击,形成选区
{
[self.calendarDelegate
setEndSelectedDate:date];
}
//每次点击,delegate中的点击计数器都要递增的
[self.calendarDelegate
updateHitCounter];
//需要触发重绘,让ios进行重新绘制,这个很关键,有一些细节,在下面会说明的
[self.calendarDelegate
repaintCalendarViews];
}
}//属于CalendarDelegate的接口函数,实现代码如下:
-(void)
repaintCalendarViews
{
//[self.tableView
setNeedsDisplay];
for(UIView
*
subview
in
self.tableView.subviews)
{
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 卫星通信机务员三级安全教育(班组级)考核试卷及答案
- 2025江苏事业单位试题及答案
- 护理专业公招考试题库及答案解析
- 塑料挤出工技术考核试卷及答案
- 2025甘肃省事业单位e类试题及答案
- 合同条款及合同格式
- 钟表文物修复师技能比武考核试卷及答案
- 物流服务师职业技能考核试卷及答案
- 电影放映员基础知识考核试卷及答案
- 琴身箱体制作工入职考核试卷及答案
- 2025年“国学小名士”知识线上竞答题库500题(含答案)
- 2025年河北水利发展集团有限公司公开招聘工作人员41名笔试参考题库附带答案详解
- 脏腑手法调理培训课件
- 2025年度宁波法院面向全市基层法院公开遴选员额法官5人考试参考题库及答案解析
- 酒店消防安全培训课件
- 2025年人工智能市场渠道拓展策略方案
- 气血两虚日常护理常规
- Unit 6 A Day in the Life 大单元整体教学分析教案-2025-2026人教版七年级英语上册
- GJB827B--2020军事设施建设费用定额
- GB/T 20716.1-2025道路车辆牵引车和挂车之间的电连接器(7芯)第1部分:24 V标称电压车辆的制动系统和行走系的连接
- 2025年第十七届广东省中学生天文知识竞赛试题(含答案)
评论
0/150
提交评论