文章目錄
一.重要參數
bounds,frame,position屬於基本的幾何定位,相互之間數值變化會相互影響
anchorPoint:單位參數(0-1)表示,變形(transform)時候的變換源點
zPosition:相當於css中z-index的概念,Apple建議不要用這個來替代CALayer層次設定。
cornerRadius:圓角
二.幾何變形(Transforming a Layer's Geometry)
1.用CATransform3D系列方法
//將CGAffineTransform轉換成CATransform3D layer1_1.transform=CATransform3DMakeAffineTransform(CGAffineTransformMakeScale(1, -1)); //CATransform3D系列方法 layer1_1.transform=CATransform3DMakeScale(-1, 1, 1); layer1_1.transform=CATransform3DScale(CATransform3DMakeScale(-2, 1, 1), -1, 1, 1); layer1_1.transform=CATransform3DIdentity;
2.修改CATransform3D的data structure
CATransform3D trans=CATransform3DIdentity; NSLog(@"%f",trans.m44); //不能是1除以,一定要1.0除以1000(zDistance) //wrong trans.m34=1/100.00 trans.m34=-1.0/1000; trans = CATransform3DTranslate(trans, 0, 0, -240); trans = CATransform3DRotate(trans, d2r(90), 1, 0, 0); trans = CATransform3DTranslate(trans, 0, 0, 240); [layer1 setTransform:trans];
3.key-valre設定key Paths(rotation,scale,translation)
[layer1 setValue:[NSNumber numberWithInt:200] forKeyPath:@"transform.translation.x"];
三.Layer數層結構(Layer-Tree Hierarchy)
1.add,insert,remove,replace來進行樹狀結構的構建
2.重設(Reposition and Resizing)layer
a)你可以通過修改第一點提到的幾個參數來調整大小。
b)needsDisplayOnBoundsChange設定YES,則在bounds變化時,自動調用setNeedsDisplay,調用layer的display的方法,或者直接顯示調用setNeedsDisplay(不能使用setNeedsDisplayInRect),也會調用自訂layer的display方法。或者是setNeesLayout,調用layer的layoutSubLayers。(PS:無法使用layoutManager和autoResizingMask方法,這個是Mac
OS上的方法。)
c)在UIView的layoutSubviews裡實現自訂layout的方法。
3.masksToBounds設定YES,則裁剪subLayers。否則不裁剪,subLayers可以超過superLayer的bounds。
4.Action
當更改layer的參數或者將layer添加,刪除,隱藏,替換時觸發
觸發方法可以是子類化CALayer,-(id<CAAction>)actionForKey:(NSString*)key;代理方式-(id<CAAction>)actionForLayer:(CALayer*) forKey:(NSString*)key
-(id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event{ CATransition *anim=nil; if([event isEqualToString:@"contents"]){ anim=[CATransition animation]; anim.duration=2; anim.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; anim.type=@"cube"; anim.subtype=kCATransitionFromRight; } return anim; //不執行任何動畫 //return nil;}
蘋果文檔種提供了修改sublayers預設動畫的方法
NSMutableDictionary *cActions=[NSMutableDictionary dictionaryWithDictionary:[layer1_1 actions]]; [cActions setObject:[NSNull null] forKey:@"sublayers"]; layer1_1.actions=cActions;
實現CAAction Protocol
代理類中實現方法runActionForKey:object:arguments
-(void)runActionForKey:(NSString *)event object:(id)anObject arguments:(NSDictionary *)dict{ NSLog(@"runActionForKey:\"%@\" object:%@ arguments:%@", event, anObject, dict); CATransition *anim=nil; if([event isEqualToString:@"test"]){ anim=[CATransition animation]; anim.duration=2; anim.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; anim.type=@"cube"; anim.subtype=kCATransitionFromRight; } [(CALayer*)anObject addAnimation:anim forKey:@"contets"];}
觸發runActionForKey
//添加自訂的action以及對應的觸發方法所在的代理,應該可以定義一個動畫子類(CAAnimation) layer1.actions = [NSDictionary dictionaryWithObjectsAndKeys:[[FCLayerDelegate alloc]init], @"test", nil]; //實際觸發的地方,當設定test參數,其實最後觸發的是runActionForKey種的立方體旋轉 [layer1 setValue:[NSNumber numberWithInt:19] forKey:@"test"];
四.提供Layer內容(Providing Layer Content)
1.contents屬性設定CGImageRef
layer1.contents=(id)image1.CGImage;
2.用delegate來提供內容
a)displayLayer:(CALayer*)
b)-(void)drawLayer:(CALayer*) inContext:(CGContextRef)
//聲明代理@interface FCLayerDeledegate : NSObject@end//實現代理@implementation FCLayerDelegate//覆蓋這個方法,就不會執行後面的方法,2者取其一,根據傳入的值來判斷下一步操作-(void)displayLayer:(CALayer *)layer{NSLog(@"%@",[layer valueForKey:@"test"]);}-(void)drawLayer:(CALayer *)ly inContext:(CGContextRef)context{ CGContextSetFillColorWithColor(context, [[UIColor redColor]CGColor]);CGContextFillRect(context, ly.bounds);}@end
//執行代碼CALayer* layer1_3=[CALayer layer]; layer1_3.delegate=[[FCLayerDelegate alloc]init];[layer1_3 setValue:@"testString" forKey:@"test"];[layer1_3 setNeedsDisplay];
ps:以上方法用於CALayer本身(非自己子類)
3) 用子類提供內容
a)display
b)drawInContext:(CGContextRef)
和前面代理模式差不多,只不過少傳layer參數,因為layer就是其本身(self);
4)內容的布局
預設情況下,內容圖片是會鋪開充滿整個layer的bounds的,如果要使得contents按照原來尺寸顯示在layer中,就要設定ContentsGravity
大致2種參數
1.位置參數,注意,ios座標是y是相反的,所以bottom就是top
2.設定鋪滿方式,3種
layer1_3.contentsGravity=kCAGravityBottomLeft;layer1_3.contents=(id)image3.CGImage;//layer1_3.contentsGravity=kCAGravityResizeAspectFill;
PS:此外contentsCenter這個屬性在鋪滿方式的參數下可以指定展開地區,文檔中指出是指定展開的範圍,但是似乎沒有作用。暫時忽略。
五.動畫
1.動畫類簇
a)CAMediaTiming protocol的主要參數
speed:執行速度,如果速度為2,則一個10秒的duration,則只需要5秒完成。子類參數相對父類參數的,如果子類是2,父類是2,則需要2.5秒完成
repeatCount|repeatDuration:重複的次數和重複的間隔 ,如果repeatCount設定成 1e100f則無限重複
fillMode:決定動畫結束時候的狀態。要和removeOnCompletion參數一起設定才有效。動畫結束後的狀態並沒有影響layer的位置,其實layer還在原來的地方沒變。
此外還有duration,autoreverses,beginTime,timeOffset
b)CAAnimation
timingFunction:指定一個CAMediaTimingFunction,有2種提供方式,一種是常量如kCAMediaTimingFunctionLinear,另外一種是自訂2個control points來定製一個時間曲線
functionWithControlPoints::::。
delegate:2個代理方法animationDidStart:和animationDidStop:finished:
2.一般動畫
任何CALayer的animated的屬性或者CTM改變時候都是動畫形式來過渡的,這個稱為implicit Animation。這裡不多做介紹
主要介紹下(Explicit Animation)顯示動畫
a)CABasicAnimation 針對某個屬性的設定變化
CABasicAnimation *anim; anim=[CABasicAnimation animationWithKeyPath:@"transform.rotation.y"]; anim.duration=8; anim.autoreverses=NO; anim.repeatCount=2; anim.fromValue=[NSNumber numberWithFloat:d2r(0)]; anim.toValue=[NSNumber numberWithFloat:d2r(-90)]; // layer1.transform=CATransform3DTranslate(CATransform3DIdentity, 0, 0, 100); [layer1 addAnimation:anim forKey:@"layer1Rotation"];
b)CAKeyframeAnimation
主要畫面格(key frame)有2種方式提供:
參數path,建立CGPathRef
path:CGPathRef 一般用於移動複雜的位置,如下例:
CGMutablePathRef path=CGPathCreateMutable(); CGPathMoveToPoint(path, NULL, 40, 420); CGPathAddCurveToPoint(path, NULL, 40, 40, 160, 40, 160, 420); CGPathAddCurveToPoint(path, NULL, 160, 40, 280, 40, 280, 420); CAKeyframeAnimation *anim=[CAKeyframeAnimation animationWithKeyPath:@"position"]; anim.path=path; anim.duration=3; //映像在移動過程中是否旋轉 anim.rotationMode=kCAAnimationRotateAuto; //動畫結束時候狀態,我動畫結束在哪裡就停在哪裡 anim.removedOnCompletion = NO; anim.fillMode=kCAFillModeForwards; //設定keyTimes path加了2個點,所以將duration設定成了2段,前面一段快3/10,後面一段慢7/10 anim.calculationMode=kCAAnimationLinear; anim.keyTimes=[NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],[NSNumber numberWithFloat:0.3],[NSNumber numberWithFloat:1.0], nil]; [layer1 addAnimation:anim forKey:@"nm1"]; CGPathRelease(path);
參數values:array of objects
如果objects是CGImage,key-path是"contents"的話,則是映像切換。
CAKeyframeAnimation *anim=[CAKeyframeAnimation animationWithKeyPath:@"contents"]; anim.duration=3; anim.values=[NSArray arrayWithObjects:(id)image1.CGImage,(id)image2.CGImage,(id)image3.CGImage,nil]; [layer1 addAnimation:anim forKey:@"nm2"];
如果objects是CATransform3D的,key-path是“transform”,則是變換座標
CAKeyframeAnimation *anim2=[CAKeyframeAnimation animationWithKeyPath:@"transform"]; anim2.duration=3; anim2.repeatCount=1000; anim2.autoreverses=YES; CATransform3D trans=CATransform3DScale(CATransform3DIdentity, 1.5, 1.5, 1); CATransform3D trans2=CATransform3DScale(CATransform3DIdentity, 1, 1, 1); anim2.values=[NSArray arrayWithObjects:[NSValue valueWithCATransform3D:trans],[NSValue valueWithCATransform3D:trans2],nil]; [layer1 addAnimation:anim2 forKey:@"nm3"];
如果2個同時設定,還可以同時看到動畫效果。比方說上述2個就可以看到圖片切換並且放大和縮小的效果。
不要針對frame和size等參數設定keyframe動畫,似乎沒有效果。position是可以的。
3.動畫事務(Transaction)
[CATransaction begin]; [CATransaction setValue:[NSNumber numberWithFloat:.5f] forKey:kCATransactionAnimationDuration]; layer1.position=CGPointMake(0, 0); layer1.opacity=0; [CATransaction commit];
通過事務可以修改implicit 動畫的時間,否則沒有辦法修改。
4.動畫過渡(Transition)
//CATransition CATransition *animation = [CATransition animation]; animation.delegate = self; animation.duration = 2; animation.timingFunction = UIViewAnimationCurveEaseInOut; //animation.type = kCATransitionFade; animation.type = @"cube"; animation.subtype = kCATransitionFromLeft; //2個view交換 NSUInteger green = [[self.view subviews] indexOfObject:self.greenView]; NSUInteger blue = [[self.view subviews] indexOfObject:self.blueView]; [self.view exchangeSubviewAtIndex:green withSubviewAtIndex:blue]; [layer1 addAnimation:animation forKey:@"animation"];
過渡有view的過渡和layer的過渡,2者是有區別的,網上有個例子transition包含了一共12種變換,相當有用。此外還可以設定startProgress和endProgress,這樣就可以讓動畫執行到某個特定位置,比如說pageCurUp的話,就可以翻到一半,相當的酷哦!
六.CALayer的子類們
隨著iOS 5.0的更新,CALayer添加了新的成員,我稍微研究了一些,現在做下總結,有的是新的,有的則是5.0以前的。
1.CAReplicatorLayer
顧名思義,這個類是用來建立重複的layer,並且可以根據設定的參數,進行一個漸層
//每個單元的小layers設定 CALayer *layer1 = [CALayer layer]; layer1.position = CGPointMake(10,200); layer1.bounds = CGRectMake(0, 0, 20, 20); layer1.backgroundColor = [[UIColor yellowColor] CGColor]; layer1.cornerRadius = 2; layer1.shadowColor = [[UIColor blackColor] CGColor]; layer1.shadowRadius = 4.0f; layer1.shadowOffset = CGSizeMake(0.0f, 3.0f); layer1.shadowOpacity = .8; layer1.opacity = 0.8; layer1.borderColor = [[UIColor whiteColor] CGColor]; layer1.borderWidth = 2.0; //設定父容器 CAReplicatorLayer *xLayer = [CAReplicatorLayer layer]; xLayer.instanceCount = 11;//產生多少個字元素 xLayer.instanceDelay = .2;//延遲產生 xLayer.instanceGreenOffset = -.03;//綠色位移量 xLayer.instanceRedOffset = -.02;//紅色位移量 xLayer.instanceBlueOffset = -.01;//藍色位移量 xLayer.instanceAlphaOffset = -.05; xLayer.instanceTransform =CATransform3DMakeTranslation(25, 0, 200);//元素位移位移量 [xLayer addSublayer:layer1];//將元素加入 xLayer.preservesDepth = YES;//superLayer是否啟用3D景深計算 //設定景深 CATransform3D transform = CATransform3DIdentity; transform.m34 = -1 / 2000.0f; xLayer.transform=transform;
甚至可以對layer加入組合式的動畫,從而各個元素會有序的一起動畫,主要的方法如下:
這個是針對layer的instanceTransform,從而可以影響每一個cell進行同樣動畫的移動,下面是進行平移的動畫
- (CABasicAnimation *)pushAnimation { CABasicAnimation *pushAnim = [CABasicAnimation animationWithKeyPath:@"instanceTransform"]; pushAnim.fromValue =[NSValue valueWithCATransform3D:CATransform3DMakeTranslation(SUBLAYER_WIDTH+INTERSPACE, 0, 0)]; pushAnim.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeTranslation(SUBLAYER_WIDTH+INTERSPACE+60, 0, 0)]; pushAnim.duration = 3.0; pushAnim.autoreverses = YES; pushAnim.repeatCount = HUGE_VAL; pushAnim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; return pushAnim;}
2.CAEmitterLayer&CAEmitterCell
網上有很多粒子相關的代碼,比如雪花的飄動,燃燒的火焰,煙霧效果,其實都是通過這個類的設定來實現的,用法和CAReplicatorLayer差不多,主要介紹下參數
// Configure the particle emitter to the top edge of the screenCAEmitterLayer *snowEmitter = [CAEmitterLayer layer];snowEmitter.emitterPosition = CGPointMake(self.view.bounds.size.width / 2.0, -30);//設定發射位置,這個和emitterMode有關 snowEmitter.emitterZPosition=50;//發射點z的位置,這個和emitterMode有關snowEmitter.emitterSize= CGSizeMake(self.view.bounds.size.width * 2.0, 0.0);//發射點的範圍,這個和emitterMode有關snowEmitter.emitterDepth=100; //從線上發射 // snowEmitter.emitterMode= kCAEmitterLayerOutline; //snowEmitter.emitterShape= kCAEmitterLayerLine; //從一個立方體內發射出,這樣的話雪花會有不同的大小在3D的情況下 snowEmitter.emitterMode= kCAEmitterLayerVolume;snowEmitter.emitterShape= kCAEmitterLayerCuboid;//設定發射單元CAEmitterCell *snowflake = [CAEmitterCell emitterCell];snowflake.birthRate= 3.0;//每秒發生1個snowflake.lifetime= 120.0;//存留時間為120秒snowflake.velocity= 20;//初始速度snowflake.velocityRange = 10;//初始速度的隨機範圍snowflake.yAcceleration = 10;//y軸加速度,當然還有z,x軸//snowflake.emissionRange = -0.5 * M_PI;// 發射角度範圍 ,這個參數一設定,似乎方向只能朝螢幕內,就是z的負軸 snowflake.spin=0.0;snowflake.spinRange=- 0.25 * M_PI;// 自旋轉範圍snowflake.contents= (id) [[UIImage imageNamed:@"flake"] CGImage];snowflake.color= [[UIColor colorWithRed:0.600 green:0.658 blue:0.743 alpha:1.000] CGColor]; // Make the flakes seem inset in the backgroundsnowEmitter.shadowOpacity = 1.0;snowEmitter.shadowRadius = 0.0;snowEmitter.shadowOffset = CGSizeMake(0.0, 1.0);snowEmitter.shadowColor = [[UIColor whiteColor] CGColor]; //snowflake.emissionLatitude=-M_PI_2;//按照Z軸旋轉 snowEmitter.preservesDepth=YES; CATransform3D transform = CATransform3DIdentity; transform.m34 = -1.0/300.0f; self.view.layer.transform=transform; // Add everything to our backing layer below the UIContol defined in the storyboardsnowEmitter.emitterCells = [NSArray arrayWithObject:snowflake];[self.view.layer insertSublayer:snowEmitter atIndex:0];
主要是3DZ軸方向的應用,如果越靠近你的視野,則圖片更大,這個效果可以通過設定父Layer的景深來實現。
參考文檔:
Animation Types and Timing Programming Guide
Core Animation Programming Guide