純屬原創(chuàng),轉(zhuǎn)載請(qǐng)標(biāo)明出處,謝謝
首先我們先看一下需求
按照MAC的系統(tǒng)時(shí)鐘仿照寫(xiě)一個(gè)類(lèi)似的時(shí)鐘效果

然后我們著手分析一個(gè)這個(gè)如何實(shí)現(xiàn)
首先這個(gè)不需要和用戶(hù)有任何交互,所以我們推薦使用CALayer隱式動(dòng)畫(huà)去完成
然后我們讓設(shè)計(jì)人員先畫(huà)出一個(gè)表盤(pán)給我們,設(shè)置他的長(zhǎng)寬都為200
其次,我們就要通過(guò)需求來(lái)描述CALayer的anchorPoint與position點(diǎn)在哪里
至于這兩個(gè)點(diǎn)是什么(也就是“錨點(diǎn)”),徹底理解position與anchorPoint - yongyinmg的專(zhuān)欄 - 博客頻道 - CSDN.NET,這篇文章說(shuō)的很清楚了,這里就不跟大家解釋
我們可以看到,所有的layer都是基于錨點(diǎn)去旋轉(zhuǎn)的,也就是表盤(pán)的中心點(diǎn),那么position屬性也就是表盤(pán)的長(zhǎng)寬的一半,由于anchorPoint的X,Y屬性取值范圍是0-1之間,以秒針舉例,如果我們想讓秒針站立在表盤(pán)中上,那么秒針的X即為0.5,Y為1
在設(shè)置下秒針的bound,寬度為1,長(zhǎng)度為表盤(pán)的半徑-20
這樣我們可以輕松的將秒針針添加到表盤(pán)上
CALayer * secondL = [CALayer layer];
secondL.backgroundColor = [UIColor redColor].CGColor ;
// 設(shè)置錨點(diǎn)
secondL.anchorPoint = CGPointMake(0.5, 1);
secondL.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);
secondL.bounds = CGRectMake(0, 0, 1, kClockW * 0.5 - 20);
[_clockView.layer addSublayer:secondL];
_secondLayer = secondL;
同理,時(shí)針和分針也是一樣,只不過(guò)長(zhǎng)度和顏色,圓角不一樣
CALayer * layer = [CALayer layer];
layer.backgroundColor = [UIColor blackColor].CGColor ;
// 設(shè)置錨點(diǎn)
layer.anchorPoint = CGPointMake(0.5, 1);
layer.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);
layer.bounds = CGRectMake(0, 0, 4, kClockW * 0.5 - 20);
layer.cornerRadius = 4;
[_clockView.layer addSublayer:layer];
_mintueLayer = layer;
CALayer * layer = [CALayer layer];
layer.backgroundColor = [UIColor blackColor].CGColor ;
// 設(shè)置錨點(diǎn)
layer.anchorPoint = CGPointMake(0.5, 1);
layer.position = CGPointMake(kClockW * 0.5, kClockW * 0.5);
layer.bounds = CGRectMake(0, 0, 4, kClockW * 0.5 - 40);
layer.cornerRadius = 4;
[_clockView.layer addSublayer:layer];
_hourLayer = layer;
這里一定要注意的是添加順序,一般來(lái)講秒針在最上層,所以秒針的layer是最后添加的
現(xiàn)在UI層我們已經(jīng)添加好了,接下來(lái)就該考慮如何讓時(shí)鐘動(dòng)起來(lái)了
讓時(shí)鐘動(dòng)起來(lái),我們先要知道時(shí)分秒他們?cè)诒肀P(pán)上所謂轉(zhuǎn)動(dòng)的角度
通過(guò)小學(xué)運(yùn)算可以得知,秒針在一秒鐘內(nèi)在表盤(pán)里轉(zhuǎn)動(dòng)的度數(shù)是6度(一個(gè)圓形是360度,360除以60),拿現(xiàn)在的系統(tǒng)的秒數(shù)*6就是現(xiàn)在秒針的具體度數(shù)
分針在一分鐘內(nèi)在表盤(pán)轉(zhuǎn)動(dòng)的度數(shù)也是6度
而時(shí)針呢,一小時(shí)時(shí)針變化為360除以12等于30度,但是有個(gè)問(wèn)題,常理知,時(shí)鐘并不是只有在分針轉(zhuǎn)到12點(diǎn)的時(shí)候才變化,它表盤(pán)里也會(huì)隨著分針的變化而變化(比如說(shuō)1:30,時(shí)針不可能只還停留在1這個(gè)位置,等到2:00的時(shí)候才突然變化)所以這個(gè)角度還需要加上分針對(duì)它的變化。這個(gè)怎么算呢,拿30度除以60現(xiàn)在的系統(tǒng)分鐘數(shù)就可以得到了,也就是0.5現(xiàn)在的分鐘數(shù)就是時(shí)鐘在以分鐘作單位時(shí)轉(zhuǎn)動(dòng)的度數(shù)
30*系統(tǒng)小時(shí)+0.5*系統(tǒng)分鐘
由于這些都是不可變化的,我們來(lái)抽成宏
// 1秒6度(秒針)
#define perSecondA 6
// 1分鐘6度(分針)
#define perMintueA 6
// 1小時(shí)30度(時(shí)針)
#define perHourA 30
// 每分鐘時(shí)針轉(zhuǎn)(30 / 60 °)
#define perMinHourA 0.5
有了這些角度之后,剩下的就很好做了,我們?cè)趘iewDidLoad的里添加一個(gè)定時(shí)器
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timeChange) userInfo:nil repeats:YES];
然后在這個(gè)timeChange方法里,設(shè)置時(shí)分秒的旋轉(zhuǎn)形變就可以了,這里注意,CATransform3DMakeRotation這個(gè)接受的弧度而不是角度,我們?cè)俪槌鲆粋€(gè)宏來(lái)將角度轉(zhuǎn)化為弧度
#define angle2radion(angle) ((angle) / 180.0 * M_PI)
- (void)timeChange{
// 獲取當(dāng)前系統(tǒng)時(shí)間
NSCalendar * calender = [NSCalendar currentCalendar];
NSDateComponents * cmp = [calender components:NSCalendarUnitSecond | NSCalendarUnitMinute | NSCalendarUnitHour fromDate:[NSDate date]];
CGFloat second = cmp.second;
CGFloat secondA = (second * perSecondA) ;
NSInteger minute = cmp.minute;
CGFloat mintuteA = minute * perMintueA ;
NSInteger hour = cmp.hour;
CGFloat hourA = hour * perHourA + minute * perMinHourA;
_secondLayer.transform = CATransform3DMakeRotation(angle2radion(secondA), 0, 0, 1);
_mintueLayer.transform = CATransform3DMakeRotation(angle2radion(mintuteA), 0, 0, 1);
_hourLayer.transform = CATransform3DMakeRotation(angle2radion(hourA), 0, 0, 1);
}
做到這里,基本上已經(jīng)大功告成,讓我們運(yùn)行一下

時(shí)鐘已經(jīng)隨著系統(tǒng)時(shí)間轉(zhuǎn)了起來(lái),還可以在修改一些別的樣式,到時(shí)候就看開(kāi)發(fā)者如何發(fā)揮了
本Dome的Github地址
https://github.com/DriverWang/YCClock