iOS开发一款小巧简洁的日历控件

简介:

iOS开发一款小巧简洁的日历控件

一、引言

        日 历是iOS开发中有时会用到的一个UI控件,网上开源的代码也很多,我浏览过一些,大致有两种模式,一种是日历的逻辑由开发者自己实现,通过计算闰年与平 年来确定月份天数,另外一种模式是通过NSDate这个时间类,来获取日历的信息。我个人认为后一种更加安全,代码性能也会更加优质,下面就是我用这种模 式实现的一个日历控件。

二、设计思路

1、先来看下效果吧

151734_UMxO_2340880.png        151735_BsEo_2340880.png           151735_ZUgv_2340880.png

2、我们需要实现的功能

(1)每行7天,对应星期,列数为将当前月显示完全

(2)今日标红

(3)点击的日期背景填充

(4)提供特殊标记,用于标记计划日,节日等

(5)左右无限翻页,直到世界起源和末日

 3、设计步骤

(1)设计一个日历模型

?
1
2
3
4
5
6
7
#import "YHBaseModel.h"
 
@interface YHBaseDateModel : YHBaseModel
@property(nonatomic,strong)NSString * year;
@property(nonatomic,strong)NSString * month;
@property(nonatomic,strong)NSString * day;
@end

(2)向系统的NSDate类中添加一些扩展方法,便于我们使用

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//头文件部分
@interface NSDate (YHBaseCalendar)
/**
  *获取当前月的天数
  */
- (NSUInteger)YHBaseNumberOfDaysInCurrentMonth;
/**
  *获取本月第一天
  */
- (NSDate *)YHBaseFirstDayOfCurrentMonth;
//下面这些方法用于获取各种整形的数据
/**
  *确定某天是周几
  */
-( int )YHBaseWeekly;
/**
  *年月日 时分秒
  */
-( int )getYear;
-( int )getMonth;
-( int )getDay;
-( int )getHour;
-( int )getMinute;
-( int )getSecond;
@end
 
//实现部分
@implementation NSDate (YHBaseCalendar)
-(NSUInteger)YHBaseNumberOfDaysInCurrentMonth{
      return  [[NSCalendar currentCalendar] rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:self].length;  
}
- (NSDate *)YHBaseFirstDayOfCurrentMonth
{
     NSDate *startDate = nil;
     BOOL  ok = [[NSCalendar currentCalendar] rangeOfUnit:NSMonthCalendarUnit startDate:&startDate interval:NULL forDate:self];
     NSAssert1(ok, @ "Failed to calculate the first day of the month based on %@" , self);
     return  startDate;
}
-( int )YHBaseWeekly{
      return  ( int )[[NSCalendar currentCalendar] ordinalityOfUnit:NSDayCalendarUnit inUnit:NSWeekCalendarUnit forDate:self];
}
 
 
 
-( int )getYear{
     NSCalendar *calendar = [NSCalendar currentCalendar];
     NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
     NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];
     return  ( int )dateComponent.year;
}
-( int )getMonth{
     NSCalendar *calendar = [NSCalendar currentCalendar];
     NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
     NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];
     return  ( int )dateComponent.month;
}
-( int )getDay{
     NSCalendar *calendar = [NSCalendar currentCalendar];
     NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
     NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];
     return  ( int )dateComponent.day;
}
-( int )getHour{
     NSCalendar *calendar = [NSCalendar currentCalendar];
     NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
     NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];
     return  ( int )dateComponent.hour;
}
-( int )getMinute{
     NSCalendar *calendar = [NSCalendar currentCalendar];
     NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
     NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];
     return  ( int )dateComponent.minute;
}
-( int )getSecond{
     NSCalendar *calendar = [NSCalendar currentCalendar];
     NSUInteger unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit;
     NSDateComponents *dateComponent = [calendar components:unitFlags fromDate:self];
     return  ( int )dateComponent.second;
}
@end

(3)设计我们的UI控件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//头文件部分
@interface YHBaseCalendarView : YHBaseView
@property(nonatomic,strong)NSDate * currentDate;
//标记数组 用于标记特殊日期 这个数组中存放的必须是YHBaseDateModel 对象
@property(nonatomic,strong)NSArray * markArray;
@property(nonatomic,weak)id<YHBaseCalendarViewDelegate> delegate;
@end
//实现部分
@interface YHBaseCalendarView()<UIScrollViewDelegate>
{
     //星期
     UIView * _headView;
     //日历的展示
     UIView * _bodyViewL;
     UIView * _bodyViewM;
     UIView * _bodyViewR;
     //滑动功能的支持
     UIScrollView * _scrollView;
     NSDate * _today;
     
     YHBaseDateModel * _selectModel;
}
@end
@implementation YHBaseCalendarView
-( void )reloadView{
     _currentDate = [NSDate date];
     _today = [NSDate date];
     _selectModel = [[YHBaseDateModel alloc]init];
     _selectModel.year = [NSString stringWithFormat:@ "%d" ,[_today getYear]];
     _selectModel.month =[NSString stringWithFormat:@ "%d" ,[_today getMonth]];
     _selectModel.day = [NSString stringWithFormat:@ "%d" ,[_today getDay]];
     _scrollView = [[UIScrollView alloc]initWithFrame:CGRectMake(0, 30, self.frame.size.width, self.frame.size.height)];
     _scrollView.contentSize = CGSizeMake(3*self.frame.size.width, 0);
     _scrollView.contentOffset = CGPointMake(self.frame.size.width, 0);
     _scrollView.pagingEnabled=YES;
     _scrollView.delegate=self;
     [self addSubview:_scrollView];
     _bodyViewL = [[UIView alloc]initWithFrame:CGRectMake(0, 0, _scrollView.frame.size.width, _scrollView.frame.size.height)];
     [_scrollView addSubview:_bodyViewL];
     _bodyViewM = [[UIView alloc]initWithFrame:CGRectMake(_scrollView.frame.size.width,0,  _scrollView.frame.size.width, _scrollView.frame.size.height)];
     [_scrollView addSubview:_bodyViewM];
     _bodyViewR = [[UIView alloc]initWithFrame:CGRectMake(_scrollView.frame.size.width*2, 0, _scrollView.frame.size.width, _scrollView.frame.size.height)];
     [_scrollView addSubview:_bodyViewR];
     //展示星期
     _headView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, 30)];
     _headView.backgroundColor = [UIColor redColor];
     NSArray * weekArray = @[@ "SUN" ,@ "MON" ,@ "TUES" ,@ "WED" ,@ "THUR" ,@ "FRI" ,@ "SAT" ];
     for  ( int  i=0; i<7; i++) {
         UILabel * label = [[UILabel alloc]initWithFrame:CGRectMake(self.frame.size.width/7*i, 0, self.frame.size.width/7, 30)];
         if  (i!=0&&i!=6) {
             label.backgroundColor = [UIColor redColor];
         } else {
             label.backgroundColor = [UIColor purpleColor];
         }
         label.text=weekArray[i];
         label.textAlignment = NSTextAlignmentCenter;
         label.layer.borderWidth=1;
         label.layer.borderColor = [[UIColor grayColor]CGColor];
         label.font = [UIFont boldSystemFontOfSize:16];
         label.layer.borderColor=[[UIColor grayColor] CGColor];
         label.textColor = [UIColor whiteColor];
         label.layer.borderWidth = 1;
         [_headView addSubview:label];
     }
     [self addSubview:_headView];
     
     [self creatViewWithData:_currentDate onView:_bodyViewM];
     [self creatViewWithData:[YHBaseDateTools getPreviousframDate:_currentDate] onView:_bodyViewL];
     [self creatViewWithData:[YHBaseDateTools getNextMonthframDate:_currentDate] onView:_bodyViewR];
}
//核心的构造方法
-( void )creatViewWithData:(id)data onView:(UIView *)bodyView{
     NSDate * currentDate = (NSDate *)data;
     //获取当前月有多少天
     int  monthNum = ( int )[currentDate YHBaseNumberOfDaysInCurrentMonth];
     //获取第一天的日期
     NSDate * firstDate = [currentDate YHBaseFirstDayOfCurrentMonth];
     //确定这一天是周几
     int  weekday = [firstDate YHBaseWeekly];
     //确定创建多少行
     int  weekRow=0;
     int  tmp=monthNum;
     if  (weekday!=7) {
         weekRow++;
         tmp=monthNum-(7-weekday);
     }
     weekRow += tmp/7;
     weekRow += (tmp%7)?1:0;
     //开始创建按钮
     /**
      *这里的逻辑是有问题的,应该设计成cell的复用机制,而不应该重复耗性能的创建 有时间在优化
      */
#warning 可以优化哦 
     NSArray * array = [bodyView subviews];
     for  (UIView * v in array) {
         [v removeFromSuperview];
     }
     int  nextDate = 1;
     //行
     for  ( int  i=0; i<weekRow; i++) {
         //列
         for  ( int  j=0; j<7; j++) {
             //先进行上个月余天的创建
             UIButton * btn;
             if  (weekday!=7&&(i*7+j)<weekday) {
                 //获取上个月有多少天
                 NSDate * preDate = [YHBaseDateTools getPreviousframDate:currentDate];
                 int  preDays = ( int )[preDate YHBaseNumberOfDaysInCurrentMonth];
                 btn =[[UIButton alloc]initWithFrame:CGRectMake(self.frame.size.width/7*j, self.frame.size.width/7*i, self.frame.size.width/7, self.frame.size.width/7)];
                 [btn setTitle:[NSString stringWithFormat:@ "%d" ,preDays-weekday+j+1] forState:UIControlStateNormal];
                 [btn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
                 [bodyView addSubview:btn];
             } else  if ((i*7+j+1-(weekday==7?0:weekday))<=monthNum){
                 btn =[[UIButton alloc]initWithFrame:CGRectMake(self.frame.size.width/7*j, self.frame.size.width/7*i, self.frame.size.width/7, self.frame.size.width/7)];
                 [btn setTitle:[NSString stringWithFormat:@ "%d" ,(i*7+j+1-(weekday==7?0:weekday))] forState:UIControlStateNormal];
                 [btn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
                 [bodyView addSubview:btn];
             } else {
                 btn =[[UIButton alloc]initWithFrame:CGRectMake(self.frame.size.width/7*j, self.frame.size.width/7*i, self.frame.size.width/7, self.frame.size.width/7)];
                 [btn setTitle:[NSString stringWithFormat:@ "%d" ,nextDate++] forState:UIControlStateNormal];
                 [btn setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
                 [bodyView addSubview:btn];
             }
             //将今天的日期标出
             if  ([currentDate getYear]==[_today getYear]&&[currentDate getMonth]==[_today getMonth]&&[btn.titleLabel.text intValue]==[_today getDay]&&!CGColorEqualToColor([btn.titleLabel.textColor CGColor], [[UIColor grayColor] CGColor])) {
                 [btn setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
             }
             //是否进行自定义标记
             /**
              *if中的颜色比较 是为了让上月与下月的余日不产生bug
              */
             if  (_markArray!=nil) {
                 for  ( int  i=0; i<_markArray.count; i++) {
                     YHBaseDateModel * model = _markArray[i];
                     if  ([currentDate getYear]==[model.year intValue]&&[currentDate getMonth]==[model.month intValue]&&[btn.titleLabel.text intValue]==[model.day intValue]&&!CGColorEqualToColor([btn.titleLabel.textColor CGColor], [[UIColor grayColor] CGColor])) {
                         btn.layer.borderColor = [[UIColor grayColor]CGColor];
                         btn.layer.borderWidth=1;
                     }
                 }
             }
             //是否进行选中标记
             if  ([_selectModel.year intValue]==[currentDate getYear]&&[_selectModel.month intValue]==[currentDate getMonth]&&[_selectModel.day intValue]==[btn.titleLabel.text intValue]&&!CGColorEqualToColor([btn.titleLabel.textColor CGColor], [[UIColor grayColor] CGColor])) {
                 btn.backgroundColor = [UIColor cyanColor];
             }
             if  (!CGColorEqualToColor([btn.titleLabel.textColor CGColor], [[UIColor grayColor] CGColor])) {
                 //添加点击事件
                 [btn addTarget:self action:@selector(clickBtn:) forControlEvents:UIControlEventTouchUpInside];
             }
            
         }
     }
     
}
//这个方法中进行重构
-( void )scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
     if  (scrollView.contentOffset.x==0) { //向前翻页了
         _currentDate = [YHBaseDateTools getPreviousframDate:_currentDate];
         _scrollView.contentOffset=CGPointMake(scrollView.frame.size.width, 0);
         
         [self creatViewWithData:_currentDate onView:_bodyViewM];
         [self creatViewWithData:[YHBaseDateTools getPreviousframDate:_currentDate] onView:_bodyViewL];
         [self creatViewWithData:[YHBaseDateTools getNextMonthframDate:_currentDate] onView:_bodyViewR];
         
     } else  if  (scrollView.contentOffset.x==scrollView.frame.size.width){
         
     } else  if  (scrollView.contentOffset.x==scrollView.frame.size.width*2){
         _currentDate = [YHBaseDateTools getNextMonthframDate:_currentDate];
         _scrollView.contentOffset=CGPointMake(scrollView.frame.size.width, 0);
         
         [self creatViewWithData:_currentDate onView:_bodyViewM];
         [self creatViewWithData:[YHBaseDateTools getPreviousframDate:_currentDate] onView:_bodyViewL];
         [self creatViewWithData:[YHBaseDateTools getNextMonthframDate:_currentDate] onView:_bodyViewR];
     }
     scrollView.userInteractionEnabled=YES;
     if  ([self.delegate respondsToSelector:@selector(YHBaseCalendarViewScrollEndToDate:)]) {
         YHBaseDateModel * model = [[YHBaseDateModel alloc]init];
         model.year = [NSString stringWithFormat:@ "%d" ,[_currentDate getYear]];
         model.month = [NSString stringWithFormat:@ "%d" ,[_currentDate getMonth]];
         model.day = [NSString stringWithFormat:@ "%d" ,[_currentDate getDay]];
         [self.delegate YHBaseCalendarViewScrollEndToDate:model];
     }
}
 
 
-( void )scrollViewDidScroll:(UIScrollView *)scrollView{
     scrollView.userInteractionEnabled=NO;
}
 
//点击事件
-( void )clickBtn:(UIButton *)btn{
     _selectModel.year = [NSString stringWithFormat:@ "%d" ,[_currentDate getYear]];
     _selectModel.month = [NSString stringWithFormat:@ "%d" ,[_currentDate getMonth]];
     _selectModel.day = btn.titleLabel.text;
     [self creatViewWithData:_currentDate onView:_bodyViewM];
     [self creatViewWithData:[YHBaseDateTools getPreviousframDate:_currentDate] onView:_bodyViewL];
     [self creatViewWithData:[YHBaseDateTools getNextMonthframDate:_currentDate] onView:_bodyViewR];
     if  ([self.delegate respondsToSelector:@selector(YHBaseCalendarViewSelectAtDateModel:)]) {
         [self.delegate YHBaseCalendarViewSelectAtDateModel:_selectModel];
     }
     
}
 
@end

(4)为用户交互设计的协议

?
1
2
3
4
@protocol YHBaseCalendarViewDelegate<NSObject>
-( void )YHBaseCalendarViewSelectAtDateModel:(YHBaseDateModel *)dateModel;
-( void )YHBaseCalendarViewScrollEndToDate:(YHBaseDateModel *)dateModel;
@end
目录
相关文章
|
1月前
|
API 数据安全/隐私保护 iOS开发
利用uni-app 开发的iOS app 发布到App Store全流程
利用uni-app 开发的iOS app 发布到App Store全流程
84 3
|
3月前
|
存储 iOS开发
iOS 开发,如何进行应用的本地化(Localization)?
iOS 开发,如何进行应用的本地化(Localization)?
122 2
|
3月前
|
存储 数据建模 数据库
IOS开发数据存储:什么是 UserDefaults?有哪些替代方案?
IOS开发数据存储:什么是 UserDefaults?有哪些替代方案?
39 0
|
3月前
|
安全 编译器 Swift
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
IOS开发基础知识: 对比 Swift 和 Objective-C 的优缺点。
91 2
|
3月前
|
API 开发工具 iOS开发
iOS 开发高效率工具包:10 大必备工具
iOS 开发高效率工具包:10 大必备工具
47 1
|
3月前
|
API 数据安全/隐私保护 iOS开发
利用uni-app 开发的iOS app 发布到App Store全流程
利用uni-app 开发的iOS app 发布到App Store全流程
52 1
|
6天前
|
API 定位技术 iOS开发
IOS开发基础知识:什么是 Cocoa Touch?它在 iOS 开发中的作用是什么?
【4月更文挑战第18天】**Cocoa Touch** 是iOS和Mac OS X应用的核心框架,包含面向对象库、运行时系统和触摸优化工具。它提供Mac验证的开发模式,强调触控接口和性能,涵盖3D图形、音频、网络及设备访问API,如相机和GPS。是构建高效iOS应用的基础,对开发者至关重要。
9 0
|
21天前
|
开发工具 Swift iOS开发
利用SwiftUI构建动态用户界面:iOS开发新范式
【4月更文挑战第3天】 随着苹果不断推进其软件开发工具的边界,SwiftUI作为一种新兴的编程框架,已经逐渐成为iOS开发者的新宠。不同于传统的UIKit,SwiftUI通过声明式语法和强大的功能组合,为创建动态且响应式的用户界面提供了一种更加简洁高效的方式。本文将深入探讨如何利用SwiftUI技术构建具有高度自定义能力和响应性的用户界面,并展示其在现代iOS应用开发中的优势和潜力。
|
2月前
|
监控 API Swift
用Swift开发iOS平台上的上网行为管理监控软件
在当今数字化时代,随着智能手机的普及,人们对于网络的依赖日益增加。然而,对于一些特定场景,如家庭、学校或者企业,对于iOS设备上的网络行为进行管理和监控显得尤为重要。为了满足这一需求,我们可以利用Swift语言开发一款iOS平台上的上网行为管理监控软件。
194 2
|
3月前
|
数据可视化 iOS开发
iOS 开发,什么是 Interface Builder(IB)?如何使用 IB 构建用户界面?
iOS 开发,什么是 Interface Builder(IB)?如何使用 IB 构建用户界面?
40 4