为了账号安全,请及时绑定邮箱和手机立即绑定

用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(二B)

标签:
Android iOS C++
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人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消