CalendarDelegate协议中索引、月份映射关系以及UITableView中CalendarView的定位问题:
千言万语,不如再来一张图来的清晰
-(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复用。以后有机会我们来从头到尾实现一个带有上述功能的控件。
有了上面的代码,我们就可以初始化CalendarController:
- (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:
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;
}
CalenderView初始化:
- (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函数?
答案下次公布,有兴趣的,可以留言回答!呵呵呵!!!
点击查看更多内容
3人点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦