? ? ?作為一名健身愛好者,使用Keep也有三千分鐘了,最近對他們的根據(jù)日期顯示View界面很感興趣,忍不住動手仿寫了一個。示例圖:
首先來分析一下,它的日期包含三個部分,上個月的最后幾天,當(dāng)前月數(shù)的天數(shù),下個月的頭幾天。如圖:
1.上個月的最后幾天
2.下個月的頭幾天
3.中間的當(dāng)前月的天數(shù),這個不需要展示了,gif圖里面的中間顯示就是
我將界面整體分為上下兩個部分,我們一個一個來分析,我會用盡可能詳細(xì)的方法來描訴,而且我的Demo里有非常詳細(xì)的注釋。
上面的滾動條負(fù)責(zé)顯示日期,而日期的顯示通過button來實(shí)現(xiàn)。滾動條用scrollView來生成,其顯示方式為:一次顯示七個button,分別代表周末-周六,每個button都代表一個日期為幾月幾號。日期的顯示為:周幾-日期。如:星期五,七月一號則顯示為五-1。而如果button的日期為今天則顯示為:今天。
此時出現(xiàn)兩種情況,第一:若前月的第一天不是周末,就需要通過獲取上個月的天數(shù),然后計算所需要的最后幾天為幾月幾號星期幾。第二:若當(dāng)前月的最后一天不是周六,則需要顯示下個月的頭幾天為幾月幾號星期幾。
不過對于這兩個問題的解決,我們只需要獲取上個月的天數(shù)即可,詳情后面解釋。
同時,對于滾動條上的button而言,每一個button都代表一天,我們要通過button的點(diǎn)擊切換下面的頁面。所以,為了讓按鈕更好地保存日期與周幾的信息,且不會與用來識別button的tag屬性混淆,我用了一個自定義的button,只添加了兩個屬性:
//代表按鈕是幾月幾號
@property(nonatomic,assign)NSInteger weekday;
//代表按鈕是幾號
@property(nonatomic,assign)NSInteger day;
而且為了以后的開發(fā)適應(yīng)范圍更廣,此處顯示日期的滾動條寬度不為屏幕的寬度。
而對于下面的View頁面顯示,則每一個View代表一天,View頁面包含兩種:代表今天的View,不代表今天的View。不代表今天的View上有一個按鈕:回到今天,點(diǎn)擊后可跳轉(zhuǎn)到代表今天的View頁面。而代表今天的View則顯示為此處是今天的頁面。
因此根據(jù)分析,我們首先需要獲取當(dāng)前月有多少天,在計算之前我們先熟悉一下NSCalendar(日歷),我的Demo中,日歷的默認(rèn)算法都是公歷
//獲取算法為公歷的日歷
NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
而后通過公歷的日歷,獲取當(dāng)前月有多少天,核心代碼如下:
//給定當(dāng)前日期
NSDate * currentDate = [NSDate date];
//計算當(dāng)前月有多少天
NSRange range = [calendar rangeOfUnit:NSCalendarUnitDay
inUnit: NSCalendarUnitMonth
forDate:currentDate];
//range的長度即當(dāng)前月的天數(shù)
return range.length;
然后,我們?nèi)耘f通過系統(tǒng)提供NSCalendar的算法獲取當(dāng)前頁中所有天數(shù)都是周幾,但在獲取之前我們需要獲取指定格式的日期:年 - 月 - 此月的第幾天,而在獲取到此格式之前,我們還需要先確定一下當(dāng)前的時間為X年X月,詳情可參考以下代碼:
//指定日期的顯示格式為年- 月
NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
NSDate * currentDate = [NSDate date];
[formatter setDateFormat:@"yyyy-MM"];
//獲取 年 - 月
NSString * str = [formatter stringFromDate:currentDate];
//? ? NSLog(@"%@",str);
隨后可通過循環(huán)遍歷當(dāng)前月的天數(shù)來拼接格式:年 - 月 - 第幾天,核心代碼:
//聲明格式為:年 - 月 - 第幾天
[formatter setDateFormat:@"yyyy-MM-dd"];
//用來保存周幾的數(shù)組
NSMutableArray * allDaysArray = [[NSMutableArray alloc] init];
//當(dāng)前月有多少天就循環(huán)多少次
for (NSInteger i = 1; i <= dayCount; i++) {
//獲取時間格式如: 年 - 月 - 1 、 年 - 月 - 2
NSString * sr = [NSString stringWithFormat:@"%@-%ld",str,i];
NSDate *suDate = [formatter dateFromString:sr];
//通過時間自定義方法獲取第幾日為星期幾
[allDaysArray addObject:[self getweekDayWithDate:suDate]];
}
而自定義方法getweekDayWithDate里面所做的事情就很簡單了,通過日歷為公歷的NSCalendar方法來計算傳進(jìn)來的日期是星期幾:
- (id) getweekDayWithDate:(NSDate *) date
{
//指定日歷的算法為公歷
NSCalendar * calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
//獲取指定日期為周幾
NSDateComponents *comps = [calendar components:NSCalendarUnitWeekday fromDate:date];
// 若不減一 則 1 是周日,2是周一 3是周二 以此類推
// 而減一后 則 0為周日,1為周一,2為周二 以此類推
return @([comps weekday]-1);
}
至此,我們獲取到的數(shù)據(jù)有:當(dāng)前月有多少天,每天都是周幾。
而在設(shè)置滾動條之前我們還應(yīng)該先獲取當(dāng)前月的第一天是周幾,我通過將當(dāng)前月每天都是周幾返回的數(shù)據(jù)用數(shù)組保存,就很輕松的可以獲得第一天為周幾,代碼如下:
//獲取當(dāng)前月都是周幾
NSArray *dayArr = [self getAllDaysWithCalender];
//獲取一號的時候是周幾
NSInteger day =? [dayArr[0] integerValue];
隨后開始設(shè)置顯示日期的button的滾動條了,在設(shè)置時,由于當(dāng)前月第一天不是周日,就需要獲取上個月的最后幾天,因此我們需要先獲取上個月總共有多少天,依舊是取算法為公歷的日歷,隨后通過NSDateComponets計算上個月的月數(shù)是幾月,核心代碼如下:
//獲取當(dāng)前日期
NSDate * mydate = [NSDate date];
//設(shè)置關(guān)于日期的算法為上個月
NSDateComponents *adcomps = [[NSDateComponents alloc]init];
[adcomps setMonth:-1];
//設(shè)置日期格式為MM 月
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"MM"];
//通過指定算法獲取上個月,并轉(zhuǎn)換格式
NSDate *yesterMonDate = [calendar dateByAddingComponents:adcomps toDate:mydate options:0 ];
NSString *beforeDate = [dateFormatter stringFromDate:yesterMonDate];
而后通過獲取到NSCalendar提供的算法,獲取這個月中總共有多少天:
//獲取上個月有多少天
NSRange range = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:yesterMonDate];
到此,我們得到了上個月的天數(shù),可以計算需要顯示的button信息都為哪些。
我是通過for循環(huán)來計算的,for循環(huán)條件為int i=0; i<day;i++,day為當(dāng)前月的第一號為周幾,因?yàn)閺闹苋臻_始排,所以周幾就代表前面有幾天,獲取上個月的天數(shù)減去i值即為所需顯示的前幾天的日期為幾號,而且是從周日開始排,我一開始還以為要再去計算得到周日那天幾號,算到這步才醒悟不用算~
for循環(huán)的內(nèi)部核心代碼:
//按鈕的frame會在添加時重新設(shè)置,所以此處設(shè)為0即可
//我為了能看到按鈕的正確生成而保存著
//創(chuàng)建所需
ButtonOfWeek *btn = [[ButtonOfWeek alloc]initWithFrame:CGRectMake(i*50, i*30, 50, 30)];
//設(shè)置背景色為隨機(jī)色
btn.backgroundColor = [UIColor colorWithRed:(arc4random_uniform(256)/256.0) green:(arc4random_uniform(256)/256.0) blue:(arc4random_uniform(256)/256.0) alpha:1];
//獲取上個月的天數(shù)
NSInteger num = [self getNumberOfDaysInYesterMonth];
//獲取顯示的前幾天的日期
NSInteger dayNum = num - i;
//? ? ? ? NSLog(@"%ld",dayNum);
//獲取上個月的月數(shù)跟計算出的幾號拼接成weekday值
//用來識別按鈕代表的是幾月幾號
NSString *dayNumOfStr = [NSString stringWithFormat:@"%ld%02ld",self.offNum,dayNum];
//? ? ? ? NSLog(@"%@",dayNumOfStr);
//? ? ? ? NSLog(@"%@",dayNumOfStr);
//設(shè)置按鈕代表第幾號,如7月23號的23號
btn.day = dayNum;
//設(shè)置按鈕的weekday值,也就是日期號
//如:七月一號為701號,七月十三號713號
btn.weekday = [dayNumOfStr integerValue];
//將按鈕添加到數(shù)組
[buttonArr addObject:btn];
在這里解釋一下,buttonArr用來保存滾動條上所有的button,我們先保存所需要生成的所有button,再通過for循環(huán)遍歷添加并重新布局.
在上個月的button顯示之后即為當(dāng)前月的Button信息,獲取的方法無外乎剛才說的那幾種,直接通過for循環(huán)既可以獲取所需的全部button,核心代碼如下,num值為當(dāng)前月的天數(shù):
for (int i = 0; i < num; i++) ?{
ButtonOfWeek *btn = [[ButtonOfWeek alloc]initWithFrame:CGRectMake(i%7*50, i/7*30,50, 30)];
//設(shè)置背景顏色隨機(jī)
btn.backgroundColor = [UIColor colorWithRed:(arc4random_uniform(256)/256.0) green:(arc4random_uniform(256)/256.0) blue:(arc4random_uniform(256)/256.0) alpha:1];
//設(shè)置日期
NSDate *date = [NSDate date];
//獲取當(dāng)前的月數(shù)
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
[dateFormatter setDateFormat:@"MM"];
//將獲取到的月數(shù)轉(zhuǎn)成字符串
NSString *str = [dateFormatter stringFromDate:date];
//通過月數(shù)幾號拼接成字符串weekday
//設(shè)置weekday值為701,712這種格式,方便后面判斷
NSString *weekStr = [NSString stringWithFormat:@"%@%02d",str,i+1];
//注意:轉(zhuǎn)換成integerValue后,0716會變成716
NSInteger weekday = [weekStr integerValue];
//設(shè)置weekday值
btn.weekday = weekday;
//設(shè)置按鈕顯示的是幾號
btn.day = i+1;
//? ? ? ? NSLog(@"%ld",tag);
//將按鈕添加到數(shù)組
[buttonArr addObject:btn];
}
獲取了當(dāng)前月所需顯示的button后,就要考慮若當(dāng)前月的月底不為周六,則需要補(bǔ)充幾個代表下月的button信息。
解決思路很簡單:當(dāng)前按鈕如果顯示到周一,就代表要填充6個按鈕為下個月的按鈕,所以通過求余計算要填充幾個按鈕,NSInteger afterNum = 7-buttonArr.count%7;
而如果當(dāng)前月的月底正好為周六,就代表afterNum的值為7,此時不添加Button,核心代碼如下:
NSInteger afterNum = 7-buttonArr.count%7;
//如果要顯示的是七天,就一天也不顯示
if (afterNum != 7) {
for (int i = 0; i<afterNum;i++){
ButtonOfWeek *btn = [[ButtonOfWeek alloc]init];
//設(shè)置要顯示的日期,如1號,2號
btn.day = self.buttonArr.count%7+i;
//背景顏色隨機(jī)
btn.backgroundColor = [UIColor colorWithRed:(arc4random_uniform(256)/256.0) green:(arc4random_uniform(256)/256.0) blue:(arc4random_uniform(256)/256.0) alpha:1];
//到此保存了所有的按鈕
[buttonArr addObject:btn];
}
}
獲取了滾動條上所有的Button就可以進(jìn)行添加了,在書寫添加前,先確定一下按鈕的的信息設(shè)置:tag用來識別按鈕,weekday保存的是Button代表的幾月幾號,day表示button代表的是周幾。而weekday與day的值我們都已經(jīng)獲取并保存,tag值在循環(huán)時添加,所以代碼如下:
//設(shè)置滾動的ScrollView
UIScrollView *scrollV? = [[UIScrollView alloc]initWithFrame:CGRectMake(10, 100, 350, 33)];
//循環(huán)遍歷保存的按鈕,依次取出并設(shè)置
//按鈕的設(shè)置
for (int i = 0; i < buttonArr.count; i++) {
//獲取按鈕重新設(shè)置Frame
ButtonOfWeek *btn = buttonArr[i];
btn.frame = CGRectMake(i*50, 0, 50, 30);
//按鈕上的星期幾就是i的值,0為周日,1為周一,依次排列
//將周幾從0、1、2格式轉(zhuǎn)成日、一、二格式
NSString *weekday = self.weekArr[i%7];
//設(shè)置按鈕的顯示文字
//如果按鈕代表的日期是今天,就顯示幾年
//否則就顯示:“日-日期”,如7月6日星期三為:“三-6”,7月3日星期日為:“日-3”
//先獲取今天是幾月幾號
NSInteger today = [self getNumberOfToday];
//根據(jù)按鈕保存的幾月幾號與今天日期進(jìn)行判斷
if (today == btn.weekday) {
[btn setTitle:[NSString stringWithFormat:@"今日"] forState:UIControlStateNormal];
//保存代表今日的按鈕
self.todayButton = btn;
}else{
[btn setTitle:[NSString stringWithFormat:@"%@-%ld",weekday,btn.day] forState:UIControlStateNormal];
}
//? ? ? ? NSLog(@"%ld",btn.weekday);
//將按鈕添加到ScrollView并延長ScrollV的滾動范圍
[scrollV addSubview:btn];
//用tag保存當(dāng)前是第幾個按鈕
btn.tag = i;
//添加點(diǎn)擊事件
[btn addTarget:self action:@selector(clickButtoOfDay:) forControlEvents:UIControlEventTouchUpInside];
//設(shè)置偏移量
scrollV.contentSize = CGSizeMake(scrollV.contentSize.width+btn.frame.size.width, 0);
}
而后開啟scrollView的滾動分頁效果(pagingEnabled),并用全局屬性保存含有顯示button的ScrollView,而后添加ScrollView到當(dāng)前View
在按鈕的下方有一個紅色標(biāo)志,紅色的標(biāo)志條為一個View,隨著按鈕的點(diǎn)擊而改變自身frame,而初始化時frame就位于按鈕的下方,高度為2。
上半部分的按鈕顯示設(shè)置完畢后,就可以動手設(shè)置下半部分的View顯示界面,關(guān)于分頁效果、frame、滾動值這些我就不多說了,詳情看下Demo即可,很簡單的。
關(guān)于顯示View的分析為:View的寬度是屏幕的寬度,有多少個button就設(shè)置多少個View,背景顏色隨機(jī),代表是不同的View。顯示結(jié)果有兩種:代表今日的VIew顯示一個Label信息,不是今日的View顯示一個Button,若需要可修改button的透明度alpha(我觀察后覺得keep里就修改了)。
View顯示內(nèi)容是否是今日的判斷,通過已保存的代表今日的按鈕(代碼中有寫)的tag值與循環(huán)時的i值來比較判斷。
核心代碼如下:
//給ScrollView的每一頁添加一個顯示View
for (int i = 0;i < self.buttonArr.count; i++) {
//設(shè)置顯示View
UIView *baskV = [[UIView alloc]initWithFrame:CGRectMake(i*W, 0, bottomScrollV.frame.size.width, bottomScrollV.frame.size.height)];
//設(shè)置View的顏色為隨機(jī)顏色
baskV.backgroundColor = [UIColor colorWithRed:(arc4random_uniform(256)/256.0) green:arc4random_uniform(256)/256.0 blue:arc4random_uniform(256)/256.0 alpha:1];
//通過代表今日的按鈕的tag值與i比較,得到當(dāng)前View代表的是今日界面
if (i == self.todayButton.tag) {
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(100, 100, 150, 100)];
label.text = @"這里是今日界面!";
[baskV addSubview:label];
}else{
//給每個View添加一個按鈕為回到今天
UIButton *backToday = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 44)];
[backToday setTitle:@"回到今日" forState:UIControlStateNormal];
//回到今日的按鈕點(diǎn)擊事件
[backToday addTarget:self action:@selector(clickBackToday:) forControlEvents:UIControlEventTouchUpInside];
backToday.backgroundColor = [UIColor greenColor];
backToday.alpha = 0.3;
[baskV addSubview:backToday];
}
//將View添加到ScrollV上
[bottomScrollV addSubview:baskV];
}
接下來,是對顯示日期的button的點(diǎn)擊事件與View中button的點(diǎn)擊事件實(shí)現(xiàn)。
View中的Button點(diǎn)擊事件很簡單:
#pragma mark - 點(diǎn)擊返回今日的按鈕
-(void)clickBackToday:(UIButton *)sender{
//滾動上面的ScrollView
//滾動范圍X為一頁的寬度 * 滾動的頁數(shù)
[self.topScrollV setContentOffset:CGPointMake(self.todayButton.tag/7*(7*self.todayButton.frame.size.width), 0) animated:YES];
//滾動下面的ScrollView
[self clickButtoOfDay:self.todayButton];
}
?顯示日期的Button的點(diǎn)擊事件
//因?yàn)槠屏繜o論如何都要計算
//所以我選擇ScrollView方式,不過也可以用CollectionView來寫
-(void)clickButtoOfDay:(ButtonOfWeek *)sender{
//改變標(biāo)志View的frame
CGSize lineSize = self.lineView.frame.size;
self.lineView.frame = CGRectMake(sender.tag*sender.frame.size.width, self.lineView.frame.origin.y, lineSize.width, lineSize.height);
//滾動底部的ScrollView
[self.bottomScrollV setContentOffset:CGPointMake(sender.tag*W, 0) animated:YES];
}
Demo地址:https://github.com/DrunkenMouse/copyKeepOfDate
最后說幾點(diǎn):
按鈕的狀態(tài)包括普通、高亮、選中、禁選(enable與userInteractionEnabled,前者會進(jìn)入disabled狀態(tài)后者不會)
而我觀看keep頁面上的按鈕狀態(tài)則分為以下幾種:
禁選時修改alpha值改變透明度,并修改字體顏色,或通過enable的disabled狀態(tài)
休息日時字體顏色為黑色,背景顏色為白色
非休息日時字體顏色為黑色,背景顏色為灰色并切圓,或直接設(shè)置一張圓形圖片(若切圓,建議開啟保存緩存方法)
訓(xùn)練結(jié)束時按鈕添加一個小圖標(biāo),小圖標(biāo)可通過選中與否切換圖片顯示,或按鈕上加一個imageView\Button來顯示圖標(biāo),然后hidden來決定顯示與否
如果是代表當(dāng)前日期的按鈕,則修改字體顏色為綠色