CALayer
概念
在iOS中,你能看得見(jiàn)摸得著的東西基本上都是UIView,比如一個(gè)按鈕、一個(gè)文本標(biāo)簽、一個(gè)文本輸入框、一個(gè)圖標(biāo)等等,這些都是UIView,其實(shí)UIView之所以能顯示在屏幕上,完全是因?yàn)樗鼉?nèi)部的一個(gè)圖層
在創(chuàng)建UIView對(duì)象時(shí),UIView內(nèi)部會(huì)自動(dòng)創(chuàng)建一個(gè)圖層(即CALayer對(duì)象),通過(guò)UIView的layer屬性可以訪問(wèn)這個(gè)層 @property(nonatomic,readonly,retain) CALayer *layer;
當(dāng)UIView需要顯示到屏幕上時(shí),會(huì)調(diào)用drawRect:方法進(jìn)行繪圖,并且會(huì)將所有內(nèi)容繪制在自己的圖層上,繪圖完畢后,系統(tǒng)會(huì)將圖層拷貝到屏幕上,于是就完成了UIView的顯示,換句話說(shuō),UIView本身不具備顯示的功能,是它內(nèi)部的層才有顯示功能
基本屬性
寬度和高度@propertyCGRectbounds;位置(默認(rèn)指中點(diǎn),具體由anchorPoint決定)@propertyCGPointposition;錨點(diǎn)(x,y的范圍都是0-1),決定了position的含義@propertyCGPointanchorPoint;背景顏色(CGColorRef類型)@propertyCGColorRefbackgroundColor;形變屬性@propertyCATransform3Dtransform;邊框顏色(CGColorRef類型)@propertyCGColorRefborderColor;邊框?qū)挾菮propertyCGFloatborderWidth;圓角半徑@propertyCGColorRefborderColor;內(nèi)容(比如設(shè)置為圖片CGImageRef)@property(retain)idcontents;
基本使用
通過(guò)操作CALayer對(duì)象,可以很方便地調(diào)整UIView的一些外觀屬性,比如: 陰影 圓角大小 邊框?qū)挾群皖伾?… …
還可以給圖層添加動(dòng)畫(huà),來(lái)實(shí)現(xiàn)一些比較炫酷的效果
1操作layer改變UIView外觀.1.1設(shè)置陰影? ? ? ? 默認(rèn)圖層是有陰影的, 只不過(guò),是透明的self.RedView.layer.shadowOpacity=1;? ? ? ? 設(shè)置陰影的圓角self.RedView.layer.shadowRadius=10;? ? ? ? 設(shè)置陰影的顏色,把UIKit轉(zhuǎn)換成CoreGraphics框架,用.CG開(kāi)頭self.RedView.layer.shadowColor= [UIColorblueColor].CGColor;1.2.設(shè)置邊框? ? ? ? 設(shè)置圖層邊框,在圖層中使用CoreGraphics的CGColorRefself.RedView.layer.borderColor= [UIColorwhiteColor].CGColor;self.RedView.layer.borderWidth=2;1.3.設(shè)置圓角? ? ? ? 圖層的圓角半徑,圓角半徑為寬度的一半, 就是一個(gè)圓self.RedView.layer.cornerRadius=50;2.操作layer改變UIImageView的外觀.2.1設(shè)置圖形邊框self.imageView.layer.borderWidth=2;self.imageView.layer.borderColor= [UIColorwhiteColor].CGColor;2.2設(shè)置圖片的圓角半徑self.imageView.layer.cornerRadius=50;? ? ? ? 裁剪,超出裁剪區(qū)域的部分全部裁剪掉self.imageView.layer.masksToBounds=YES;? ? ? ? 注意:UIImageView當(dāng)中Image并不是直接添加在層上面的.這是添加在layer當(dāng)中的contents里.? ? ? ? 我們?cè)O(shè)置層的所有屬性它只作用在層上面.對(duì)contents里面的東西并不起作用.所以我們看不到圖片有圓角的效果.? ? ? ? 想要讓圖片有圓角的效果.可以把masksToBounds這個(gè)屬性設(shè)為YES,? ? ? ? 當(dāng)設(shè)為YES,把就會(huì)把超過(guò)根層以外的東西都給裁剪掉.3.layer的CATransform3D屬性.? ? ? 只有旋轉(zhuǎn)的時(shí)候才可以看出3D的效果.? ? ? 旋轉(zhuǎn)? ? ? x,y,z 分別代表x,y,z軸.CATransform3DMakeRotation(M_PI,1,0,0);? ? ? 平移CATransform3DMakeTranslation(x,y,z)? ? ? 縮放CATransform3DMakeScale(x,y,z);? ? ? 可以通過(guò)KVC的方式進(jìn)行設(shè)置屬性.? ? ? 但是CATransform3DMakeRotation它的值,是一個(gè)結(jié)構(gòu)體, 所以要把結(jié)構(gòu)轉(zhuǎn)成對(duì)象.NSValue*value = [NSValuevalueWithCATransform3D:CATransform3DMakeRotation(M_PI,1,0,0)];? ? ? [_imageView.layersetValue:value forKeyPath:@"transform.scale"];? ? ? 什么時(shí)候用KVC?? ? ? 當(dāng)需要做一些快速縮放,平移,二維的旋轉(zhuǎn)時(shí)用KVC.? ? ? 比如: [_imageView.layersetValue:@0.5forKeyPath:@"transform.scale"];? ? ? 快速的進(jìn)行縮放.? ? ? 后面forKeyPath屬性值不是亂寫的.蘋果文檔當(dāng)中給了相關(guān)的屬性.
UIKit -> CG
首先要知道:CALayer是定義在QuartzCore框架中的;CGImageRef、CGColorRef兩種數(shù)據(jù)類型是定義在CoreGraphics框架中的;UIColor、UIImage是定義在UIKit框架中的
其次,QuartzCore框架和CoreGraphics框架是可以跨平臺(tái)使用的,在iOS和Mac OS X上都能使用,但是UIKit只能在iOS中使用
因此,為了保證可移植性,QuartzCore不能使用UIImage、UIColor,只能使用CGImageRef、CGColorRef
不過(guò)很多情況下,可以通過(guò)UIKit對(duì)象的特定方法,得到CoreGraphics對(duì)象,比如UIImage的CGImage方法可以返回一個(gè)CGImageRef
自定義CAlayer
1.如何自定義Layer.? ? 自定義CALayer的方式創(chuàng)建UIView的方式非常相似.CALayer*layer = [CALayerlayer];? ? layer.frame=CGRectMake(50,50,100,100);? ? layer.backgroundColor= [UIColorredColor].CGColor;? ? [self.view.layeraddSublayer:layer];? ? 給layer設(shè)置圖片.? ? layer.contents= (id)[UIImageimageNamed:@"阿貍頭像"].CGImage;
UIView和CALayer的選擇
通過(guò)CALayer,就能做出跟UIImageView一樣的界面效果
對(duì)比CALayer,UIView多了一個(gè)事件處理的功能。也就是說(shuō),CALayer不能處理用戶的觸摸事件,而UIView可以.所以,如果顯示出來(lái)的東西需要跟用戶進(jìn)行交互的話,用UIView;如果不需要跟用戶進(jìn)行交互,用UIView或者CALayer都可以,當(dāng)然,CALayer的性能會(huì)高一些,因?yàn)樗倭耸录幚淼墓δ?,更加輕量級(jí)
UIView可以通過(guò)subviews屬性訪問(wèn)所有的子視圖,類似地,CALayer也可以通過(guò)sublayers屬性訪問(wèn)所有的子層
UIView可以通過(guò)superview屬性訪問(wèn)父視圖,類似地,CALayer也可以通過(guò)superlayer屬性訪問(wèn)父層
下面再看一張UIView和CALayer的關(guān)系圖:
如果兩個(gè)UIView是父子關(guān)系,那么它們內(nèi)部的CALayer也是父子關(guān)系。
position和anchorPoint
@propertyCGPointposition;用來(lái)設(shè)置CALayer在父層中的位置以父層的左上角為原點(diǎn)(0,0)@propertyCGPointanchorPoint;稱為“定位點(diǎn)”、“錨點(diǎn)”決定著CALayer身上的哪個(gè)點(diǎn)會(huì)在position屬性所指的位置以自己的左上角為原點(diǎn)(0,0)它的x、y取值范圍都是0~1,默認(rèn)值為(0.5,0.5)
如圖
隱式動(dòng)畫(huà)
根層:UIView內(nèi)部自動(dòng)關(guān)聯(lián)著的那個(gè)layer我們稱它是根層.
非根層:自己手動(dòng)創(chuàng)建的層,稱為非根層.
隱式動(dòng)畫(huà)就是當(dāng)對(duì)非根層的部分屬性進(jìn)行修改時(shí),它會(huì)自動(dòng)的產(chǎn)生一些動(dòng)畫(huà)的效果.我們稱這個(gè)默認(rèn)產(chǎn)生的動(dòng)畫(huà)為隱式動(dòng)畫(huà).
如何取消隱式動(dòng)畫(huà) 首先要了解動(dòng)畫(huà)底層是怎么做的.動(dòng)畫(huà)的底層是包裝成一個(gè)事務(wù)(很多操作綁定在一起,當(dāng)這些操作執(zhí)行完畢后,才去執(zhí)行下一個(gè)操作.)來(lái)進(jìn)行的.
開(kāi)啟事務(wù)? ? [CATransactionbegin];? ? 設(shè)置事務(wù)沒(méi)有動(dòng)畫(huà)? ? [CATransactionsetDisableActions:YES];? ? 設(shè)置動(dòng)畫(huà)執(zhí)行的時(shí)長(zhǎng)? ? [CATransactionsetAnimationDuration:2];? ? 提交事務(wù)? ? [CATransactioncommit];
每一個(gè)UIView內(nèi)部都默認(rèn)關(guān)聯(lián)著一個(gè)CALayer,我們可用稱這個(gè)Layer為Root Layer(根層)
所有的非Root Layer,也就是手動(dòng)創(chuàng)建的CALayer對(duì)象,都存在著隱式動(dòng)畫(huà)
隱式動(dòng)畫(huà)是指當(dāng)對(duì)非RootLayer的部分屬性進(jìn)行修改時(shí),默認(rèn)會(huì)自動(dòng)產(chǎn)生一些動(dòng)畫(huà)效果而這些屬性稱為Animatable,Properties(可動(dòng)畫(huà)屬性)
列舉幾個(gè)常見(jiàn)的Animatable Properties:
1->bounds:用于設(shè)置CALayer的寬度和高度。修改這個(gè)屬性會(huì)產(chǎn)生縮放動(dòng)畫(huà)
2->backgroundColor:用于設(shè)置CALayer的背景色。修改這個(gè)屬性會(huì)產(chǎn)生背景色的漸變動(dòng)畫(huà)
3->position:用于設(shè)置CALayer的位置。修改這個(gè)屬性會(huì)產(chǎn)生平移動(dòng)畫(huà)
時(shí)鐘效果
1.搭建界面.? ? ? ? 分析界面.? ? ? ? 界面上時(shí)針,分針,秒針不需要與用戶進(jìn)行交互.所以都可以使用layer方式來(lái)做.? ? ? ? 做之前要觀察時(shí)針在做什么效果.? ? ? ? 是根據(jù)當(dāng)前的時(shí)間,繞著表盤的中心點(diǎn)進(jìn)行旋轉(zhuǎn).? ? ? ? 要了解一個(gè)非常重要的知識(shí)點(diǎn).無(wú)論是旋轉(zhuǎn),縮放它都是繞著錨點(diǎn).進(jìn)行的.? ? ? ? 要想讓時(shí)針,分針,稱針顯示的中間,還要繞著中心點(diǎn)進(jìn)行旋轉(zhuǎn).? ? ? ? 那就要設(shè)置它的position和anchorPoint兩個(gè)屬性.? ? ? ? 創(chuàng)建秒針CALayer*layer = [CALayerlayer];? ? ? ? _secLayer = layer;? ? ? ? layer.bounds=CGRectMake(0,0,1,80);? ? ? ? layer.anchorPoint=CGPointMake(0.5,1);? ? ? ? layer.position=CGPointMake(_clockView.bounds.size.width*0.5,? ? _clockView.bounds.size.height*0.5);? ? ? ? layer.backgroundColor= [UIColorredColor].CGColor;? ? ? ? [_clockView.layeraddSublayer:layer];2.讓秒針開(kāi)始旋轉(zhuǎn).? ? ? ? 讓秒針旋轉(zhuǎn).所以要計(jì)算當(dāng)前的旋轉(zhuǎn)度是多少?? ? ? ? 當(dāng)前的旋轉(zhuǎn)角度為:當(dāng)前的時(shí)間 * 每秒旋轉(zhuǎn)多少度.? ? ? ? 計(jì)算每一秒旋轉(zhuǎn)多少度.60秒轉(zhuǎn)一圈360度360除以60就是每一秒轉(zhuǎn)多少度.每秒轉(zhuǎn)6度.? ? ? ? 獲取當(dāng)前的時(shí)間? ? ? ? 創(chuàng)建日歷類NSCalendar*calendar = [NSCalendarcurrentCalendar];? ? ? ? 把日歷類轉(zhuǎn)換成一個(gè)日期組件? ? ? ? 日期組件(年,月,日,時(shí),分,秒)? ? ? ? component:日期組件有哪些東西組成,他是一個(gè)枚舉,里面有年月日時(shí)分秒? ? ? ? fromDate:當(dāng)前的日期NSDateComponents*cmp = [calendar components:NSCalendarUnitSecondfromDate:[NSDatedate]];? ? ? ? 我們的秒就是保存在日期組件里面,它里面提供了很多get方法.NSIntegersecond = cmp.second;? ? ? ? 那么當(dāng)前秒針旋轉(zhuǎn)的角度就是? ? ? ? 當(dāng)前的秒數(shù)乘以每秒轉(zhuǎn)多少度.? ? ? ? second * perSecA? ? ? ? 還得要把角度轉(zhuǎn)換成弧度.? ? ? ? 因?yàn)橄旅娣轴?時(shí)針也得要用到, 就把它抽出一個(gè)速參數(shù)的宏.#define angle2Rad(angle) ((angle) / 180.0 * M_PI)讓它每隔一秒旋轉(zhuǎn)一次.所以添加一個(gè)定時(shí)器.? ? ? ? 每個(gè)一秒就調(diào)用,旋轉(zhuǎn)秒針? ? ? ? - (void)timeChange{? ? ? ? 獲取當(dāng)前的秒數(shù)? ? ? ? 創(chuàng)建日歷類NSCalendar*calendar = [NSCalendarcurrentCalendar];? ? ? ? 把日歷類轉(zhuǎn)換成一個(gè)日期組件? ? ? ? 日期組件(年,月,日,時(shí),分,秒)? ? ? ? component:日期組件有哪些東西組成,他是一個(gè)枚舉,里面有年月日時(shí)分秒? ? ? ? fromDate:當(dāng)前的日期NSDateComponents*cmp = [calendar components:NSCalendarUnitSecondfromDate:[NSDatedate]];? ? ? ? 我們的秒就是保存在日期組件里面,它里面提供了很多get方法.NSIntegersecond = cmp.second;? ? ? ? 秒針旋轉(zhuǎn)多少度.CGFloatangel = angle2Rad(second * perSecA);? ? ? ? 旋轉(zhuǎn)秒針self.secondL.transform=CATransform3DMakeRotation(angel,0,0,1);? ? ? ? }? ? ? ? 運(yùn)行發(fā)現(xiàn)他會(huì)一下只就調(diào)到某一個(gè)時(shí)間才開(kāi)始旋轉(zhuǎn)? ? ? ? 一開(kāi)始的時(shí)候就要來(lái)到這個(gè)方法,獲取當(dāng)前的秒數(shù)把它定位好.? ? ? ? 要在添加定時(shí)器之后就調(diào)用一次timeChange方法.3.添加分針? ? ? ? 快速拷貝一下,然后添加一個(gè)分針成員屬性.? ? ? ? 修改寬度,修改顏色? ? ? ? 也得要讓它旋轉(zhuǎn),? ? ? ? 要算出每分鐘轉(zhuǎn)多少度? ? ? ? 轉(zhuǎn)60分鐘剛好是一圈? ? ? ? 所以每一分鐘也是轉(zhuǎn)6度.? ? ? ? 獲取當(dāng)前多少分?? ? ? ? 同樣是在日期組件里面獲得? ? ? ? 里面有左移符號(hào),右移符號(hào).他就可以用一個(gè)并運(yùn)算? ? ? ? 現(xiàn)在同時(shí)讓他支持秒數(shù)和分 后面直接加上一個(gè) |NSDateComponents*cmp = [calendar components:NSCalendarUnitSecond|NSCalendarUnitMinutefromDate:[NSDatedate]];CGFloatminueteAngel = angle2Rad(minute * perMinuteA);self.minueL.transform=CATransform3DMakeRotation(minueteAngel,0,0,1);4.添加時(shí)針? ? ? ? ? 同樣復(fù)制之前的,添加一個(gè)小時(shí)屬性? ? ? ? 小時(shí)轉(zhuǎn)多少度? ? ? ? 當(dāng)前是多少小時(shí),再計(jì)算先每一小時(shí)轉(zhuǎn)多少度.12個(gè)小時(shí)轉(zhuǎn)一圈.360除以12,每小時(shí)轉(zhuǎn)30度? ? ? ? 時(shí)針旋轉(zhuǎn)多少度CGFloathourAngel = angle2Rad(hour * perHourA);? ? ? ? 旋轉(zhuǎn)時(shí)針self.hourL.transform=CATransform3DMakeRotation(hourAngel,0,0,1);? ? ? ? 直接這樣寫會(huì)有問(wèn)題? ? ? ? 就是沒(méi)轉(zhuǎn)一分鐘,小時(shí)也會(huì)移動(dòng)一點(diǎn)點(diǎn)? ? ? ? 接下來(lái)要算出,每一分鐘,小時(shí)要轉(zhuǎn)多少度60分鐘一小時(shí).一小時(shí)轉(zhuǎn)30度.30除以60,就是每一分鐘,時(shí)針轉(zhuǎn)多少度.0.5時(shí)針旋轉(zhuǎn)多少度CGFloathourAngel = angle2Rad(hour * perHourA + minute * perMinuteHourA);? ? ? ? 旋轉(zhuǎn)時(shí)針self.hourL.transform=CATransform3DMakeRotation(hourAngel,0,0,1);