iOS 封装一个简单的日历控件
2017-11-02 本文已影响455人
雪_晟
先看下效果图:
日历.gif
看了一些第三方,改动起来不容易,找了一个demo,跟着思路走了一遍,所以还是动手自己封装一遍。封装尽量保持代码简洁。
首先是布局。
分为三个模块:
布局.png
分为LXCalendarHearder
,LXCalendarWeekView
以及collectionView
封装的日历控件。
header
和weekView
就不用多说了,最重要的是collectionView
的模型建立。有的是把一个月的数据放在一个模型里,想了想还是把每一天的数据单独建立一个模型。
模型的建立
首先是根据当前的日期拿到当月的数据模型,以及上个月下个月的数据模型,因为可能有需要设置上个月和下个月的显示以及灰度处理。
month
模型:
@property (nonatomic, strong) NSDate *monthDate; //!< 传入的 NSDate 对象,该 NSDate 对象代表当前月的某一日,根据它来获得其他数据
@property (nonatomic, assign) NSInteger totalDays; //!< 当前月的天数
@property (nonatomic, assign) NSInteger firstWeekday; //!< 标示第一天是星期几(0代表周日,1代表周一,以此类推)
@property (nonatomic, assign) NSInteger year; //!< 所属年份
@property (nonatomic, assign) NSInteger month; //!< 当前月份
- (instancetype)initWithDate:(NSDate *)date;
关于获取上个月下个月的日期,demo
里有。
DayModel
模型:
@interface LXCalendarDayModel : NSObject
@property (nonatomic, assign) NSInteger totalDays; //!< 当前月的天数
@property (nonatomic, assign) NSInteger firstWeekday; //!< 标示第一天是星期几(0代表周日,1代表周一,以此类推)
@property (nonatomic, assign) NSInteger year; //!< 所属年份
@property (nonatomic, assign) NSInteger month; //!< 当前月份
@property (nonatomic, assign) NSInteger day; //每天所在的位置
@property(nonatomic,assign)BOOL isLastMonth;//属于上个月的
@property(nonatomic,assign)BOOL isNextMonth;//属于下个月的
@property(nonatomic,assign)BOOL isCurrentMonth;//属于当月
@property(nonatomic,assign)BOOL isToday;//今天
@property(nonatomic,assign)BOOL isSelected;//是否被选中
根据month模型建立DayModel模型:
LXCalendarDayModel *model =[[LXCalendarDayModel alloc]init];
//配置外面属性
[self configDayModel:model];
model.firstWeekday = firstWeekday;
model.totalDays = totalDays;
model.month = monthModel.month;
model.year = monthModel.year;
//上个月的日期
if (i < firstWeekday) {
model.day = lastMonthModel.totalDays - (firstWeekday - i) + 1;
model.isLastMonth = YES;
}
//当月的日期
if (i >= firstWeekday && i < (firstWeekday + totalDays)) {
model.day = i -firstWeekday +1;
model.isCurrentMonth = YES;
//标识是今天
if ((monthModel.month == [[NSDate date] dateMonth]) && (monthModel.year == [[NSDate date] dateYear])) {
if (i == [[NSDate date] dateDay] + firstWeekday - 1) {
model.isToday = YES;
}
}
}
//下月的日期
if (i >= (firstWeekday + monthModel.totalDays)) {
model.day = i -firstWeekday - nextMonthModel.totalDays +1;
model.isNextMonth = YES;
}
上面的处理主要是把上个月和下个月需要显示的日期和当月的日期全部塞进DayModel
模型里。这样我们对cell
的处理只需要根据DayModel
即可。
细节说明
demo
中只是使用了一个collectionView
然后添加左右清扫手势,给collectionView
的layer
做一个假假的动画实现翻页效果。
关于日历demo中设置了6 行,具体为什么参考日历的12月份。
最后
demo中设置了一些常见的属性,可以自行配置,需要注意的是在属性设置之后才可以处理数据。
使用方法:
self.calenderView =[[LXCalendarView alloc]initWithFrame:CGRectMake(20, 80, Device_Width - 40, 0)];
self.calenderView.currentMonthTitleColor =[UIColor hexStringToColor:@"2c2c2c"];
self.calenderView.lastMonthTitleColor =[UIColor hexStringToColor:@"8a8a8a"];
self.calenderView.nextMonthTitleColor =[UIColor hexStringToColor:@"8a8a8a"];
self.calenderView.isHaveAnimation = NO;
self.calenderView.isCanScroll = YES;
self.calenderView.isShowLastAndNextBtn = NO;
self.calenderView.isShowLastAndNextDate = YES;
self.calenderView.todayTitleColor =[UIColor redColor];
self.calenderView.selectBackColor =[UIColor greenColor];
[self.calenderView dealData];
self.calenderView.backgroundColor =[UIColor whiteColor];
[self.view addSubview:self.calenderView];
self.calenderView.selectBlock = ^(NSInteger year, NSInteger month, NSInteger day) {
NSLog(@"%ld年 - %ld月 - %ld日",year,month,day);
};
补充
代码中选中的日期在滑动会清除选中日期,修复代码如下:
[self.monthdataA enumerateObjectsUsingBlock:^(LXCalendarDayModel * obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ((obj.year == self.selectModel.year) && (obj.month == self.selectModel.month) && (obj.day == self.selectModel.day)) {
obj.isSelected = YES;
}
}];
如果是自定义日历,动画效果会超出显示区域,此时设置
self.calenderView.layer.masksToBounds = YES;
裁剪动画区域即可。
最新代码请移步demo 地址。
demo地址:LXCalendar