轉(zhuǎn)載:http://www.cnblogs.com/jingdizhiwa/p/5601240.html
1.geometryFlipped設(shè)置為yes,則子圖層或者子視圖本來相對(duì)于左上角放置 改為 相對(duì)于左下角放置;
2.contents
3.contentGravity:
kCAGravityCenter
kCAGravityTop
kCAGravityBottom
kCAGravityLeft
kCAGravityRight
kCAGravityTopLeft
kCAGravityTopRight
kCAGravityBottomLeft
kCAGravityBottomRight
kCAGravityResize
kCAGravityResizeAspect
kCAGravityResizeAspectFill
4.contentsScale
5.maskToBounds
6.contentsRect
7.contentsCenter
8.自己繪制寄宿圖:
方法-:繼承UIView并實(shí)現(xiàn)-drawRect:(如果你不需要寄宿圖,那就不要?jiǎng)?chuàng)建這個(gè)方法了,這會(huì)造成CPU資源和內(nèi)存的浪費(fèi),這也是為什么蘋果建議:如果沒有自定義繪制的任務(wù)就不要在子類中寫一個(gè)空的-drawRect:方法。)
方法二:CALayer有一個(gè)可選的delegate屬性,參考代碼如下:
blueLayer.delegate=self;
[blueLayer display];- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{//draw a thick red circleCGContextSetLineWidth(ctx,10.0f);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextStrokeEllipseInRect(ctx, layer.bounds);
}
我們?cè)赽lueLayer上顯式地調(diào)用了-display。不同于UIView,當(dāng)圖層顯示在屏幕上時(shí),CALayer不會(huì)自動(dòng)重繪它的內(nèi)容。它把重繪的決定權(quán)交給了開發(fā)者。
盡管我們沒有用masksToBounds屬性,繪制的那個(gè)圓仍然沿邊界被裁剪了。這是因?yàn)楫?dāng)你使用CALayerDelegate繪制寄宿圖的時(shí)候,并沒有對(duì)超出邊界外的內(nèi)容提供繪制支持。
9.布局:
frame并不是一個(gè)非常清晰的屬性,它其實(shí)是一個(gè)虛擬屬性,是根據(jù)bounds,position和transform計(jì)算而來,所以當(dāng)其中任何一個(gè)值發(fā)生改變,frame都會(huì)變化。相反,改變frame的值同樣會(huì)影響到他們當(dāng)中的值;
錨點(diǎn):
10.坐標(biāo)系:
- (CGPoint)convertPoint:(CGPoint)point fromLayer:(CALayer *)layer;- (CGPoint)convertPoint:(CGPoint)point toLayer:(CALayer *)layer;- (CGRect)convertRect:(CGRect)rect fromLayer:(CALayer *)layer;- (CGRect)convertRect:(CGRect)rect toLayer:(CALayer *)layer;
11.zPosition
增加圖層的zPosition,就可以把圖層向相機(jī)方向前置,于是它就在所有其他圖層的前面了(或者至少是小于它的zPosition值的圖層的前面);同樣適用于視圖的layer
12.Hit Testing
CALayer并不關(guān)心任何響應(yīng)鏈?zhǔn)录圆荒苤苯犹幚碛|摸事件或者手勢(shì)。但是它有一系列的方法幫你處理事件:-containsPoint:和-hitTest:。
13.自動(dòng)布局
如果想隨意控制CALayer的布局,就需要手工操作。最簡(jiǎn)單的方法就是使用CALayerDelegate如下函數(shù):
-?(void)layoutSublayersOfLayer:(CALayer?*)layer;
當(dāng)圖層的bounds發(fā)生改變,或者圖層的-setNeedsLayout方法被調(diào)用的時(shí)候,這個(gè)函數(shù)將會(huì)被執(zhí)行。這使得你可以手動(dòng)地重新擺放或者重新調(diào) 整子圖層的大小,但是不能像UIView的autoresizingMask和constraints屬性做到自適應(yīng)屏幕旋轉(zhuǎn)。
14.conrnerRadius
默認(rèn)情況下,這個(gè)曲率值只影響背景顏色而不影響背景圖片或是子圖層。不過,如果把masksToBounds設(shè)置成YES的話,圖層里面的所有東西都會(huì)被截取
15.shadowOpacity
若要改動(dòng)陰影的表現(xiàn),你可以使用CALayer的另外三個(gè)屬性:shadowColor,shadowOffset和shadowRadius
16.shadowPath屬性
我們已經(jīng)知道圖層陰影并不總是方的,而是從圖層內(nèi)容的形狀繼承而來。這看上去不錯(cuò),但是實(shí)時(shí)計(jì)算陰影也是一個(gè)非常消耗資源的,尤其是圖層有多個(gè)子圖層,每個(gè)圖層還有一個(gè)有透明效果的寄宿圖的時(shí)候。
如果你事先知道你的陰影形狀會(huì)是什么樣子的,你可以通過指定一個(gè)shadowPath來提高性能;
17.圖層蒙板
@interfaceViewController ()
@property (nonatomic, weak) IBOutlet UIImageView*imageView;@end@implementationViewController- (void)viewDidLoad
{
[super viewDidLoad];//create mask layerCALayer*maskLayer =[CALayer layer];
maskLayer.frame=self.imageView.bounds;
UIImage*maskImage = [UIImage imageNamed:@"1"];
maskLayer.contents= (__bridgeid)maskImage.CGImage;//apply mask to image layer?self.imageView.layer.mask=maskLayer;
self.imageView.image=[UIImage imageNamed:@"2"];
}@end
CALayer蒙板圖層真正厲害的地方在于蒙板圖不局限于靜態(tài)圖。任何有圖層構(gòu)成的都可以作為mask屬性,這意味著你的蒙板可以通過代碼甚至是動(dòng)畫實(shí)時(shí)生成。
18.shouldRasterize
實(shí)現(xiàn)組透明的效果,如果它被設(shè)置為YES,在應(yīng)用透明度之前,圖層及其子圖層都會(huì)被整合成一個(gè)整體的圖片,這樣就沒有透明度混合的問題了;
為了啟用shouldRasterize屬性,我們?cè)O(shè)置了圖層的rasterizationScale屬性。默認(rèn)情況下,所有圖層拉伸都是1.0, 所以如果你使用了shouldRasterize屬性,你就要確保你設(shè)置了rasterizationScale屬性去匹配屏幕,以防止出現(xiàn)Retina屏幕像素化的問題。
button2.layer.shouldRasterize =YES;
button2.layer.rasterizationScale= [UIScreen mainScreen].scale;
19.變換(CGAffineTransform)
view.transform=CGAffineTransformMakeRotation(M_PI_4) <---->self.view.layer.affineTransform=CGAffineTransformMakeRotation(M_PI_4) ;
CGAffineTransformMakeRotation(CGFloat angle)
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy)
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty)
混合變換
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
參考代碼:
CGAffineTransform transform = CGAffineTransformIdentity;//scale by 50%transform = CGAffineTransformScale(transform,0.5,0.5);//rotate by 30 degreestransform = CGAffineTransformRotate(transform, M_PI /180.0*30.0);//translate by 200 pointstransform = CGAffineTransformTranslate(transform,200,0);//apply transform to layerself.layerView.layer.affineTransform = transform;
設(shè)置CGAffineTransform的矩陣值,做任意變換:
CGAffineTransform transform =CGAffineTransformIdentity;
transform.c= -x;
transform.b= y;
20.變換(CATransform3D)
對(duì)應(yīng)layer的transform屬性
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
transform.m34---->應(yīng)用透視效果
21.sublayerTransform
CALayer有一個(gè)屬性叫做sublayerTransform。它也是CATransform3D類型,但和對(duì)一個(gè)圖層的變換不同,它影響到所有的子圖層。這意味著你可以一次性對(duì)包含這些圖層的容器做變換,于是所有的子圖層都自動(dòng)繼承了這個(gè)變換方法。
22.doubleSided
CALayer有一個(gè)叫做doubleSided的屬性來控制圖層的背面是否要被繪制。這是一個(gè)BOOL類型,默認(rèn)為YES,如果設(shè)置為NO,那么當(dāng)圖層正面從相機(jī)視角消失的時(shí)候,它將不會(huì)被繪制。
23.CAShapeLayer
使用CAShapeLayer有以下一些優(yōu)點(diǎn):
渲染快速。CAShapeLayer使用了硬件加速,繪制同一圖形會(huì)比用Core Graphics快很多。
高效使用內(nèi)存。一個(gè)CAShapeLayer不需要像普通CALayer一樣創(chuàng)建一個(gè)寄宿圖形,所以無(wú)論有多大,都不會(huì)占用太多的內(nèi)存。
不會(huì)被圖層邊界剪裁掉。一個(gè)CAShapeLayer可以在邊界之外繪制。你的圖層路徑不會(huì)像在使用Core Graphics的普通CALayer一樣被剪裁掉(如我們?cè)诘诙滤姡?/p>
不會(huì)出現(xiàn)像素化。當(dāng)你給CAShapeLayer做3D變換時(shí),它不像一個(gè)有寄宿圖的普通圖層一樣變得像素化。
24.CATextLayer
CATextLayer比UILabel有著更好的性能表現(xiàn),同時(shí)還有額外的布局選項(xiàng)并且在iOS 5上支持富文本。
讓我們編輯一下示例使用到NSAttributedString(見清單6.3).iOS 6及以上我們可以用新的NSTextAttributeName實(shí)例來設(shè)置我們的字符串屬性,但是練習(xí)的目的是為了演示在iOS 5及以下,所以我們用了Core Text,也就是說你需要把Core Text framework添加到你的項(xiàng)目中。否則,編譯器是無(wú)法識(shí)別屬性常量的。
UILabel的替代品
每一個(gè)UIView都是寄宿在一個(gè)CALayer的示例上。這個(gè)圖層是由視圖自動(dòng)創(chuàng)建和管理的,那我們可以用別的圖層類型替代它么?一旦被創(chuàng)建,我們就無(wú)法代替這個(gè)圖層了。但是如果我們繼承了UIView,那我們就可以重寫+layerClass方法使得在創(chuàng)建的時(shí)候能返回一個(gè)不同的圖層子類。UIView會(huì)在初始化的時(shí)候調(diào)用+layerClass方法,然后用它的返回類型來創(chuàng)建宿主圖層。
把CATextLayer作為宿主圖層的另一好處就是視圖自動(dòng)設(shè)置了contentsScale屬性。
25.CATransformLayer
在立方體示例,我們將通過旋轉(zhuǎn)camara來解決圖層平面化問題而不是像立方體示例代碼中用的sublayerTransform。這是一個(gè)非常不錯(cuò)的技巧,但是只能作用域單個(gè)對(duì)象上,如果你的場(chǎng)景包含兩個(gè)立方體,那我們就不能用這個(gè)技巧單獨(dú)旋轉(zhuǎn)他們了。
CATransformLayer不同于普通的CALayer,因?yàn)樗荒茱@示它自己的內(nèi)容。只有當(dāng)存在了一個(gè)能作用域子圖層的變換它才真正存在。CATransformLayer并不平面化它的子圖層,所以它能夠用于構(gòu)造一個(gè)層級(jí)的3D結(jié)構(gòu)。
26.CAGradientLayer
CAGradientLayer是用來生成兩種或更多顏色平滑漸變的。用Core Graphics復(fù)制一個(gè)CAGradientLayer并將內(nèi)容繪制到一個(gè)普通圖層的寄宿圖也是有可能的,但是CAGradientLayer的真正好處在于繪制使用了硬件加速。
27.CAReplicatorLayer
CAReplicatorLayer的目的是為了高效生成許多相似的圖層。它會(huì)繪制一個(gè)或多個(gè)圖層的子圖層,并在每個(gè)復(fù)制體上應(yīng)用不同的變換。
instanceCount屬性指定了圖層需要重復(fù)多少次。instanceTransform指定了一個(gè)CATransform3D 3D變換(這種情況下,下一圖層的位移和旋轉(zhuǎn)將會(huì)移動(dòng)到圓圈的下一個(gè)點(diǎn))。變換是逐步增加的,每個(gè)實(shí)例都是相對(duì)于前一實(shí)例布局。這就是為什么這些復(fù)制體最終不會(huì)出現(xiàn)在同一位置上;
CAReplicatorLayer真正應(yīng)用到實(shí)際程序上的場(chǎng)景比如:一個(gè)游戲中導(dǎo)彈的軌跡云,或者粒子爆炸。除此之外,還有一個(gè)實(shí)際應(yīng)用是:反射。
28.CAScrollLayer
CAScrollLayer有一個(gè)-scrollToPoint:方法,它自動(dòng)適應(yīng)bounds的原點(diǎn)以便圖層內(nèi)容出現(xiàn)在滑動(dòng)的地方。注意,這就是它做的所有事情。前面提到過,Core Animation并不處理用戶輸入,所以CAScrollLayer并不負(fù)責(zé)將觸摸事件轉(zhuǎn)換為滑動(dòng)事件,既不渲染滾動(dòng)條,也不實(shí)現(xiàn)任何iOS指定行為例如滑動(dòng)反彈(當(dāng)視圖滑動(dòng)超多了它的邊界的將會(huì)反彈回正確的地方)。
1.CATransaction
事務(wù);
UIView有兩個(gè)方法,+beginAnimations:context:和+commitAnimations,和CATransaction的+begin 和+commit方法類似。實(shí)際上在+beginAnimations:context:和+commitAnimations之間所有視圖或者圖層屬性的改變而做的動(dòng)畫都是由于設(shè)置了CATransaction的原因。
UIView封裝動(dòng)畫中的塊動(dòng)畫,對(duì)做一堆的屬性動(dòng)畫在語(yǔ)法上會(huì)更加簡(jiǎn)單,但實(shí)質(zhì)上它們都是在做同樣的事情。
基于UIView的block的動(dòng)畫允許你在動(dòng)畫結(jié)束的時(shí)候提供一個(gè)完成的動(dòng)作。CATranscation接口提供的+setCompletionBlock:方法也有同樣的功能
2.屬性動(dòng)畫實(shí)質(zhì):
當(dāng)CALayer的屬性被修改時(shí)候,它會(huì)調(diào)用-actionForKey:方法,傳遞屬性的名稱。剩下的操作都在CALayer的頭文件中有詳細(xì)的說明,實(shí)質(zhì)上是如下幾步:
圖層首先檢測(cè)它是否有委托,并且是否實(shí)現(xiàn)CALayerDelegate協(xié)議指定的-actionForLayer:forKey方法。如果有,直接調(diào)用并返回結(jié)果。
如果沒有委托,或者委托沒有實(shí)現(xiàn)-actionForLayer:forKey方法,圖層接著檢查包含屬性名稱對(duì)應(yīng)行為映射的actions字典。
如果actions字典沒有包含對(duì)應(yīng)的屬性,那么圖層接著在它的style字典接著搜索屬性名。
最后,如果在style里面也找不到對(duì)應(yīng)的行為,那么圖層將會(huì)直接調(diào)用定義了每個(gè)屬性的標(biāo)準(zhǔn)行為的-defaultActionForKey:方法。
所以一輪完整的搜索結(jié)束之后,-actionForKey:要么返回空(這種情況下將不會(huì)有動(dòng)畫發(fā)生),要么是CAAction協(xié)議對(duì)應(yīng)的對(duì)象,最后CALayer拿這個(gè)結(jié)果去對(duì)先前和當(dāng)前的值做動(dòng)畫。
通過下面一段代碼,可以理解每個(gè)UIView對(duì)它關(guān)聯(lián)的圖層是如何禁用隱式動(dòng)畫的
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"Outside: %@", [self.view actionForLayer:self.view.layer forKey:@"backgroundColor"]);
[UIView beginAnimations:nil context:nil];
NSLog(@"Inside: %@", [self.view actionForLayer:self.view.layer forKey:@"backgroundColor"]);
[UIView commitAnimations];
}
運(yùn)行結(jié)果:
2016-05-3014:48:18.614測(cè)試[10222:147396] Outside: 2016-05-3014:48:18.615測(cè)試[10222:147396] Inside:
總結(jié)一下,我們知道了如下幾點(diǎn)
UIView 關(guān)聯(lián)的圖層禁用了隱式動(dòng)畫,對(duì)這種圖層做動(dòng)畫的唯一辦法就是使用UIView的動(dòng)畫函數(shù)(而不是依賴CATransaction),或者繼承 UIView,并覆蓋-actionForLayer:forKey:方法,或者直接創(chuàng)建一個(gè)顯式動(dòng)畫(具體細(xì)節(jié)見第八章)。
對(duì)于單獨(dú)存在的圖層,我們可以通過實(shí)現(xiàn)圖層的-actionForLayer:forKey:委托方法,或者提供一個(gè)actions字典來控制隱式動(dòng)畫。
3.CATransition
CATransition繼承CAAnimation,而CAAnimation響應(yīng)CAAction協(xié)議;
CATransition *transition =[CATransition animation];
transition.type=kCATransitionPush;
transition.subtype=kCATransitionFromLeft;
self.colorLayer.actions= @{@"backgroundColor": transition};
這種方法,只是針對(duì)單獨(dú)的圖層,不能用在UIView關(guān)聯(lián)的圖層;
4.presentationLayer
每個(gè)圖層屬性的顯示值都被存儲(chǔ)在一個(gè)叫做呈現(xiàn)圖層的獨(dú)立圖層當(dāng)中,他可以通過-presentationLayer方法來訪問。這個(gè)呈現(xiàn)圖層實(shí)際上是模型圖層的復(fù)制,但是它的屬性值代表了在任何指定時(shí)刻當(dāng)前外觀效果。換句話說,你可以通過呈現(xiàn)圖層的值來獲取當(dāng)前屏幕上真正顯示出來的值
注意呈現(xiàn)圖層僅僅當(dāng)圖層首次被提交(就是首次第一次在屏幕上顯示)的時(shí)候創(chuàng)建,所以在那之前調(diào)用-presentationLayer將會(huì)返回nil。
在呈現(xiàn)圖層上調(diào)用–modelLayer將會(huì)返回它正在呈現(xiàn)所依賴的CALayer。通常在一個(gè)圖層上調(diào)用-modelLayer會(huì)返回–self(實(shí)際上我們已經(jīng)創(chuàng)建的原始圖層就是一種數(shù)據(jù)模型)。
5.顯式動(dòng)畫
在iOS中核心動(dòng)畫分為幾類:基礎(chǔ)動(dòng)畫、關(guān)鍵幀動(dòng)畫、動(dòng)畫組、轉(zhuǎn)場(chǎng)動(dòng)畫。各個(gè)類的關(guān)系大致如下:
CAAnimation:核心動(dòng)畫的基礎(chǔ)類,不能直接使用,負(fù)責(zé)動(dòng)畫運(yùn)行時(shí)間、速度的控制。CAAnimation本身并沒有做多少工作,它提供了一個(gè)計(jì)時(shí)函數(shù),一個(gè)委托(用于反饋動(dòng)畫狀態(tài))以及一個(gè) removedOnCompletion,用于標(biāo)識(shí)動(dòng)畫是否該在結(jié)束后自動(dòng)釋放(默認(rèn)YES,為了防止內(nèi)存泄露)。CAAnimation同時(shí)實(shí)現(xiàn)了一些 協(xié)議,包括CAAction(允許CAAnimation的子類可以提供圖層行為),以及CAMediaTiming
CAPropertyAnimation:屬性動(dòng)畫的基類(通過屬性進(jìn)行動(dòng)畫設(shè)置,注意是可動(dòng)畫屬性),不能直接使用。通過指定動(dòng)畫的keyPath作用于一個(gè)單一屬性,CAAnimation通常應(yīng)用于一個(gè)指定的CALayer,于是這里指的也就是一個(gè)圖層的 keyPath了。實(shí)際上它是一個(gè)關(guān)鍵路徑(一些用點(diǎn)表示法可以在層級(jí)關(guān)系中指向任意嵌套的對(duì)象),而不僅僅是一個(gè)屬性的名稱,因?yàn)檫@意味著動(dòng)畫不僅可以 作用于圖層本身的屬性,而且還包含了它的子成員的屬性,甚至是一些虛擬的屬性
CAAnimationGroup:動(dòng)畫組,動(dòng)畫組是一種組合模式設(shè)計(jì),可以通過動(dòng)畫組來進(jìn)行所有動(dòng)畫行為的統(tǒng)一控制,組中所有動(dòng)畫效果可以并發(fā)執(zhí)行。
CATransition:過渡(轉(zhuǎn)場(chǎng))動(dòng)畫,主要通過濾鏡進(jìn)行動(dòng)畫效果設(shè)置。過渡并不像屬性動(dòng)畫那樣平滑地在兩個(gè)值之間做動(dòng)畫,而是影響到整個(gè)圖層的變化。過渡動(dòng)畫首先展示之前的圖層外觀,然后通過一個(gè)交換過渡到新的外觀。
CABasicAnimation:基礎(chǔ)動(dòng)畫,通過屬性修改進(jìn)行動(dòng)畫參數(shù)控制,只有初始狀態(tài)和結(jié)束狀態(tài)。
CAKeyframeAnimation:關(guān)鍵幀動(dòng)畫,同樣是通過屬性進(jìn)行動(dòng)畫參數(shù)控制,但是同基礎(chǔ)動(dòng)畫不同的是它可以有多個(gè)狀態(tài)控制。CAKeyFrameAnimation添加了一個(gè)rotationMode的屬性。設(shè)置它為常量kCAAnimationRotateAuto,圖層將會(huì)根據(jù)曲線的切線自動(dòng)旋轉(zhuǎn)
基礎(chǔ)動(dòng)畫、關(guān)鍵幀動(dòng)畫都屬于屬性動(dòng)畫,就是通過修改屬性值產(chǎn)生動(dòng)畫效果,開發(fā)人員只需要設(shè)置初始值和結(jié)束值,中間的過程動(dòng)畫(又叫“補(bǔ)間動(dòng)畫”)由 系統(tǒng)自動(dòng)計(jì)算產(chǎn)生。和基礎(chǔ)動(dòng)畫不同的是關(guān)鍵幀動(dòng)畫可以設(shè)置多個(gè)屬性值,每?jī)蓚€(gè)屬性中間的補(bǔ)間動(dòng)畫由系統(tǒng)自動(dòng)完成,因此從這個(gè)角度而言基礎(chǔ)動(dòng)畫又可以看成是 有兩個(gè)關(guān)鍵幀的關(guān)鍵幀動(dòng)畫。
- (void)applyBasicAnimation:(CABasicAnimation *)animation toLayer:(CALayer *)layer
{//set the from value (using presentation layer if available)animation.fromValue = [layer.presentationLayer ?: layer valueForKeyPath:animation.keyPath];//update the property in advance//note: this approach will only work if toValue != nil[CATransaction begin];
[CATransaction setDisableActions:YES];
[layer setValue:animation.toValue forKeyPath:animation.keyPath];
[CATransaction commit];//apply animation to layer[layer addAnimation:animation forKey:nil];
}-(IBAction)changeColor
{//create a new random colorCGFloat red = arc4random() /(CGFloat)INT_MAX;
CGFloat green= arc4random() /(CGFloat)INT_MAX;
CGFloat blue= arc4random() /(CGFloat)INT_MAX;
UIColor*color = [UIColor colorWithRed:red green:green blue:blue alpha:1.0];//create a basic animationCABasicAnimation *animation =[CABasicAnimation animation];
animation.keyPath=@"backgroundColor";
animation.toValue= (__bridgeid)color.CGColor;//apply animation without snap-back[self applyBasicAnimation:animation toLayer:self.colorLayer];
}
6.虛擬屬性
CABasicAnimation *animation =[CABasicAnimation animation];
animation.keyPath=@"transform.rotation";
animation.duration=2.0;
animation.byValue= @(M_PI *2);
[shipLayer addAnimation:animation forKey:nil];
用transform.rotation而不是transform做動(dòng)畫的好處如下:
我們可以不通過關(guān)鍵幀一步旋轉(zhuǎn)多于180度的動(dòng)畫。
可以用相對(duì)值而不是絕對(duì)值旋轉(zhuǎn)(設(shè)置byValue而不是toValue)。
可以不用創(chuàng)建CATransform3D,而是使用一個(gè)簡(jiǎn)單的數(shù)值來指定角度。
不會(huì)和transform.position或者transform.scale沖突(同樣是使用關(guān)鍵路徑來做獨(dú)立的動(dòng)畫屬性)。
transform.rotation 屬性有一個(gè)奇怪的問題是它其實(shí)并不存在。這是因?yàn)镃ATransform3D并不是一個(gè)對(duì)象,它實(shí)際上是一個(gè)結(jié)構(gòu)體,也沒有符合KVC相關(guān)屬 性,transform.rotation實(shí)際上是一個(gè)CALayer用于處理動(dòng)畫變換的虛擬屬性。
7.動(dòng)畫組
//create group animationCAAnimationGroup *groupAnimation =[CAAnimationGroup animation];
groupAnimation.animations=@[animation1, animation2];
groupAnimation.duration=4.0;//add the animation to the color layer[colorLayer addAnimation:groupAnimation forKey:nil];
8.過渡動(dòng)畫
CAAnimation有一個(gè)type和subtype來標(biāo)識(shí)變換效果。type屬性是一個(gè)NSString類型,可以被設(shè)置成如下類型:
kCATransitionFade
kCATransitionMoveIn
kCATransitionPush
kCATransitionReveal
通過subtype來控制它們的方向,提供了如下四種類型:
kCATransitionFromRight
kCATransitionFromLeft
kCATransitionFromTop
kCATransitionFromBottom
隱式過渡
CATransision 可以對(duì)圖層任何變化平滑過渡的事實(shí)使得它成為那些不好做動(dòng)畫的屬性圖層行為的理想候選。蘋果當(dāng)然意識(shí)到了這點(diǎn),并且當(dāng)設(shè)置了CALayer的 content屬性的時(shí)候,CATransition的確是默認(rèn)的行為。但是對(duì)于視圖關(guān)聯(lián)的圖層,或者是其他隱式動(dòng)畫的行為,這個(gè)特性依然是被禁用的,但 是對(duì)于你自己創(chuàng)建的圖層,這意味著對(duì)圖層contents圖片做的改動(dòng)都會(huì)自動(dòng)附上淡入淡出的動(dòng)畫。
對(duì)圖層樹的動(dòng)畫
CATransition并不作用于指定的圖層屬性,這就是說你可以在即使不能準(zhǔn)確得知改變了什么的情況下對(duì)圖層做動(dòng)畫,例如,在不知道 UITableView哪一行被添加或者刪除的情況下,直接就可以平滑地刷新它,或者在不知道UIViewController內(nèi)部的視圖層級(jí)的情況下對(duì) 兩個(gè)不同的實(shí)例做過渡動(dòng)畫。
這些例子和我們之前所討論的情況完全不同,因?yàn)樗鼈儾簧婕暗綀D層的屬性,而且是整個(gè)圖層樹的改變--我們?cè)谶@種動(dòng)畫的過程中手動(dòng)在層級(jí)關(guān)系中添加或者移除圖層。
這里用到了一個(gè)小詭計(jì),要確保CATransition添加到的圖層在過渡動(dòng)畫發(fā)生時(shí)不會(huì)在樹狀結(jié)構(gòu)中被移除,否則CATransition將會(huì)和圖層一起被移除。一般來說,你只需要將動(dòng)畫添加到被影響圖層的superlayer。
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{//set up crossfade transitionCATransition *transition =[CATransition animation];
transition.type=kCATransitionFade;//apply transition to tab bar controller's view[self.tabBarController.view.layer addAnimation:transition forKey:nil];
}
自定義動(dòng)畫
奇怪的是蘋果通過UIView +transitionFromView:toView:duration:options:completion: 和+transitionWithView:duration:options:animations:方法提供了Core Animation的過渡特性。但是這里的可用的過渡選項(xiàng)和CATransition的type屬性提供的常量完全不同。UIView過渡方法中 options參數(shù)可以由如下常量指定:
UIViewAnimationOptionTransitionFlipFromLeft
UIViewAnimationOptionTransitionFlipFromRight
UIViewAnimationOptionTransitionCurlUp
UIViewAnimationOptionTransitionCurlDown
UIViewAnimationOptionTransitionCrossDissolve
UIViewAnimationOptionTransitionFlipFromTop
UIViewAnimationOptionTransitionFlipFromBottom
過渡動(dòng)畫做基礎(chǔ)的原則就是對(duì)原始的圖層外觀截圖,然后添加一段動(dòng)畫,平滑過渡到圖層改變之后那個(gè)截圖的效果。如果我們知道如何對(duì)圖層截圖,我們就可以使用屬性動(dòng)畫來代替CATransition或者是UIKit的過渡方法來實(shí)現(xiàn)動(dòng)畫。
事實(shí)證明,對(duì)圖層做截圖還是很簡(jiǎn)單的。CALayer有一個(gè)-renderInContext:方法,可以通過把它繪制到Core Graphics的上下文中捕獲當(dāng)前內(nèi)容的圖片,然后在另外的視圖中顯示出來。如果我們把這個(gè)截屏視圖置于原始視圖之上,就可以遮住真實(shí)視圖的所有變化, 于是重新創(chuàng)建了一個(gè)簡(jiǎn)單的過渡效果。
清單8.14演示了一個(gè)基本的實(shí)現(xiàn)。我們對(duì)當(dāng)前視圖狀態(tài)截圖,然后在我們改變?cè)家晥D的背景色的時(shí)候?qū)貓D快速轉(zhuǎn)動(dòng)并且淡出,圖8.5展示了我們自定義的過渡效果。
為 了讓事情更簡(jiǎn)單,我們用UIView -animateWithDuration:completion:方法來實(shí)現(xiàn)。雖然用CABasicAnimation可以達(dá)到同樣的效果,但是那樣的 話我們就需要對(duì)圖層的變換和不透明屬性創(chuàng)建單獨(dú)的動(dòng)畫,然后當(dāng)動(dòng)畫結(jié)束的是哦戶在CAAnimationDelegate中把coverView從屏幕中 移除。
清單8.14 用renderInContext:創(chuàng)建自定義過渡效果
-(IBAction)performTransition
{//preserve the current view snapshotUIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES,0.0);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage*coverImage =UIGraphicsGetImageFromCurrentImageContext();//insert snapshot view in front of this oneUIView *coverView =[[UIImageView alloc] initWithImage:coverImage];
coverView.frame=self.view.bounds;
[self.view addSubview:coverView];//update the view (we'll simply randomize the layer background color)CGFloat red = arc4random() /(CGFloat)INT_MAX;
CGFloat green= arc4random() /(CGFloat)INT_MAX;
CGFloat blue= arc4random() /(CGFloat)INT_MAX;
self.view.backgroundColor= [UIColor colorWithRed:red green:green blue:blue alpha:1.0];//perform animation (anything you like)[UIView animateWithDuration:1.0animations:^{//scale, rotate and fade the viewCGAffineTransform transform = CGAffineTransformMakeScale(0.01,0.01);
transform=CGAffineTransformRotate(transform, M_PI_2);
coverView.transform=transform;
coverView.alpha=0.0;
} completion:^(BOOL finished) {//remove the cover view now we're finished with it[coverView removeFromSuperview];
}];
}
CAMediaTiming協(xié)議
duration
repeatCount
repeatDuration
autoreverses
注意repeatCount和repeatDuration可能會(huì)相互沖突,所以你只要對(duì)其中一個(gè)指定非零值。對(duì)兩個(gè)屬性都設(shè)置非0值的行為沒有被定義。
beginTime
指定了動(dòng)畫開始之前的的延遲時(shí)間。這里的延遲從動(dòng)畫添加到可見圖層的那一刻開始測(cè)量,默認(rèn)是0(就是說動(dòng)畫會(huì)立刻執(zhí)行)。
speed
是一個(gè)時(shí)間的倍數(shù),默認(rèn)1.0,減少它會(huì)減慢圖層/動(dòng)畫的時(shí)間,增加它會(huì)加快速度。如果2.0的速度,那么對(duì)于一個(gè)duration為1的動(dòng)畫,實(shí)際上在0.5秒的時(shí)候就已經(jīng)完成了。
timeOffset
和beginTime類似,但是和增加beginTime導(dǎo)致的延遲動(dòng)畫不同,增加timeOffset只是讓動(dòng)畫快進(jìn)到某一點(diǎn),例如,對(duì)于一個(gè)持續(xù)1秒的動(dòng)畫來說,設(shè)置timeOffset為0.5意味著動(dòng)畫將從一半的地方開始。
fillMode
fillMode是一個(gè)NSString類型,可以接受如下四種常量:
kCAFillModeForwards
kCAFillModeBackwards
kCAFillModeBoth
kCAFillModeRemoved
默認(rèn)是kCAFillModeRemoved,當(dāng)動(dòng)畫不再播放的時(shí)候就顯示圖層模型指定的值剩下的三種類型向前,向后或者即向前又向后去填充動(dòng)畫狀態(tài),使得動(dòng)畫在開始前或者結(jié)束后仍然保持開始和結(jié)束那一刻的值。
這就對(duì)避免在動(dòng)畫結(jié)束的時(shí)候急速返回提供另一種方案(見第八章)。但是記住了,當(dāng)用它來解決這個(gè)問題的時(shí)候,需要把removeOnCompletion設(shè)置為NO,另外需要給動(dòng)畫添加一個(gè)非空的鍵,于是可以在不需要?jiǎng)赢嫷臅r(shí)候把它從圖層上移除。
暫停,倒回和快進(jìn)(圖層的speed)
設(shè)置動(dòng)畫的speed屬性為0可以暫停動(dòng)畫,但在動(dòng)畫被添加到圖層之后不太可能再修改它了,所以不能對(duì)正在進(jìn)行的動(dòng)畫使用這個(gè)屬性。給圖層添加一個(gè) CAAnimation實(shí)際上是給動(dòng)畫對(duì)象做了一個(gè)不可改變的拷貝,所以對(duì)原始動(dòng)畫對(duì)象屬性的改變對(duì)真實(shí)的動(dòng)畫并沒有作用。相反,直接用 -animationForKey:來檢索圖層正在進(jìn)行的動(dòng)畫可以返回正確的動(dòng)畫對(duì)象,但是修改它的屬性將會(huì)拋出異常。
如果移除圖層正在進(jìn)行的動(dòng)畫,圖層將會(huì)急速返回動(dòng)畫之前的狀態(tài)。但如果在動(dòng)畫移除之前拷貝呈現(xiàn)圖層到模型圖層,動(dòng)畫將會(huì)看起來暫停在那里。但是不好的地方在于之后就不能再恢復(fù)動(dòng)畫了。
一個(gè)簡(jiǎn)單的方法是可以利用CAMediaTiming來暫停圖層本身。如果把圖層的speed設(shè)置成0,它會(huì)暫停任何添加到圖層上的動(dòng)畫。類似的,設(shè)置speed大于1.0將會(huì)快進(jìn),設(shè)置成一個(gè)負(fù)值將會(huì)倒回動(dòng)畫。
通過增加主窗口圖層的speed,可以暫停整個(gè)應(yīng)用程序的動(dòng)畫。這對(duì)UI自動(dòng)化提供了好處,我們可以加速所有的視圖動(dòng)畫來進(jìn)行自動(dòng)化測(cè)試(注意對(duì)于在主窗口之外的視圖并不會(huì)被影響,比如UIAlertview)。可以在app delegate設(shè)置如下進(jìn)行驗(yàn)證:
self.window.layer.speed =100;
你也可以通過這種方式來減速,但其實(shí)也可以在模擬器通過切換慢速動(dòng)畫來實(shí)現(xiàn)。
手動(dòng)動(dòng)畫(圖層的timeOffset)
timeOffset一個(gè)很有用的功能在于你可以它可以讓你手動(dòng)控制動(dòng)畫進(jìn)程,通過設(shè)置speed為0,可以禁用動(dòng)畫的自動(dòng)播放,然后來使用timeOffset來來回顯示動(dòng)畫序列。這可以使得運(yùn)用手勢(shì)來手動(dòng)控制動(dòng)畫變得很簡(jiǎn)單。
@property (nonatomic, strong) CALayer *doorLayer;@end@implementationViewController- (void)viewDidLoad
{
[super viewDidLoad];//add the doorself.doorLayer =[CALayer layer];
self.doorLayer.frame= CGRectMake(0,0,128,256);
self.doorLayer.position= CGPointMake(150-64,150);
self.doorLayer.anchorPoint= CGPointMake(0,0.5);
self.doorLayer.contents= (__bridgeid)[UIImage imageNamed:@"bjl_list_02"].CGImage;
[self.view.layer addSublayer:self.doorLayer];//apply perspective transformCATransform3D perspective =CATransform3DIdentity;
perspective.m34= -1.0/500.0;
self.view.layer.sublayerTransform=perspective;//add pan gesture recognizer to handle swipesUIPanGestureRecognizer *pan =[[UIPanGestureRecognizer alloc] init];
[pan addTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];//pause all layer animationsself.doorLayer.speed =0.0;//apply swinging animation (which won't play because layer is paused)CABasicAnimation *animation =[CABasicAnimation animation];
animation.keyPath=@"transform.rotation.y";
animation.toValue= @(-M_PI_2);
animation.duration=1.0;
[self.doorLayer addAnimation:animation forKey:nil];
}
- (void)pan:(UIPanGestureRecognizer *)pan
{//get horizontal component of pan gestureCGFloat x =[pan translationInView:self.view].x;//convert from points to animation duration//using a reasonable scale factorx /=200.0f;//update timeOffset and clamp resultCFTimeInterval timeOffset =self.doorLayer.timeOffset;
timeOffset= MIN(0.999, MAX(0.0, timeOffset -x));
self.doorLayer.timeOffset=timeOffset;//reset pan gesture[pan setTranslation:CGPointZero inView:self.view];
}
CPU VS GPU
動(dòng)畫和屏幕上組合的圖層實(shí)際上被一個(gè)單獨(dú)的進(jìn)程管理,而不是你的應(yīng)用程序。這個(gè)進(jìn)程就是所謂的渲染服務(wù)。在iOS5和之前的版本是SpringBoard進(jìn)程(同時(shí)管理著iOS的主屏)。在iOS6之后的版本中叫做BackBoard。
一件重要的事情就是性能測(cè)試一定要用發(fā)布配置,而不是調(diào)試模式。因?yàn)楫?dāng)用發(fā)布環(huán)境打包的時(shí)候,編譯器會(huì)引入一系列提高性能的優(yōu)化,例如去掉調(diào)試符號(hào)或者移 除并重新組織代碼。你也可以自己做到這些,例如在發(fā)布環(huán)境禁用NSLog語(yǔ)句。你只關(guān)心發(fā)布性能,那才是你需要測(cè)試的點(diǎn)。
Core Animation工具也提供了一系列復(fù)選框選項(xiàng)來幫助調(diào)試渲染瓶頸:
Color Blended Layers - 這個(gè)選項(xiàng)基于渲染程度對(duì)屏幕中的混合區(qū)域進(jìn)行綠到紅的高亮(也就是多個(gè)半透明圖層的疊加)。由于重繪的原因,混合對(duì)GPU性能會(huì)有影響,同時(shí)也是滑動(dòng)或者動(dòng)畫幀率下降的罪魁禍?zhǔn)字弧?/p>
ColorHitsGreenandMissesRed - 當(dāng)使用shouldRasterizep屬性的時(shí)候,耗時(shí)的圖層繪制會(huì)被緩存,然后當(dāng)做一個(gè)簡(jiǎn)單的扁平圖片呈現(xiàn)。當(dāng)緩存再生的時(shí)候這個(gè)選項(xiàng)就用紅色對(duì)柵格 化圖層進(jìn)行了高亮。如果緩存頻繁再生的話,就意味著柵格化可能會(huì)有負(fù)面的性能影響了(更多關(guān)于使用shouldRasterize的細(xì)節(jié)見第15章“圖層 性能”)。
Color Copied Images - 有時(shí)候寄宿圖片的生成意味著Core Animation被強(qiáng)制生成一些圖片,然后發(fā)送到渲染服務(wù)器,而不是簡(jiǎn)單的指向原始指針。這個(gè)選項(xiàng)把這些圖片渲染成藍(lán)色。復(fù)制圖片對(duì)內(nèi)存和CPU使用來 說都是一項(xiàng)非常昂貴的操作,所以應(yīng)該盡可能的避免。
Color Immediately - 通常Core Animation Instruments以每毫秒10次的頻率更新圖層調(diào)試顏色。對(duì)某些效果來說,這顯然太慢了。這個(gè)選項(xiàng)就可以用來設(shè)置每幀都更新(可能會(huì)影響到渲染性 能,而且會(huì)導(dǎo)致幀率測(cè)量不準(zhǔn),所以不要一直都設(shè)置它)。
Color Misaligned Images - 這里會(huì)高亮那些被縮放或者拉伸以及沒有正確對(duì)齊到像素邊界的圖片(也就是非整型坐標(biāo))。這些中的大多數(shù)通常都會(huì)導(dǎo)致圖片的不正常縮放,如果把一張大圖當(dāng)縮 略圖顯示,或者不正確地模糊圖像,那么這個(gè)選項(xiàng)將會(huì)幫你識(shí)別出問題所在。
Color Offscreen-Rendered Yellow - 這里會(huì)把那些需要離屏渲染的圖層高亮成黃色。這些圖層很可能需要用shadowPath或者shouldRasterize來優(yōu)化。
Color OpenGL Fast Path Blue - 這個(gè)選項(xiàng)會(huì)對(duì)任何直接使用OpenGL繪制的圖層進(jìn)行高亮。如果僅僅使用UIKit或者Core Animation的API,那么不會(huì)有任何效果。如果使用GLKView或者CAEAGLLayer,那如果不顯示藍(lán)色塊的話就意味著你正在強(qiáng)制CPU 渲染額外的紋理,而不是繪制到屏幕。
Flash Updated Regions - 這個(gè)選項(xiàng)會(huì)對(duì)重繪的內(nèi)容高亮成黃色(也就是任何在軟件層面使用Core Graphics繪制的圖層)。這種繪圖的速度很慢。如果頻繁發(fā)生這種情況的話,這意味著有一個(gè)隱藏的bug或者說通過增加緩存或者使用替代方案會(huì)有提升 性能的空間。