转载:http://www.cnblogs.com/jingdizhiwa/p/5601240.html
1.geometryFlipped设置为yes,则子图层或者子视图本来相对于左上角放置 改为 相对于左下角放置;
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并实现-drawRect:(如果你不需要寄宿图,那就不要创建这个方法了,这会造成CPU资源和内存的浪费,这也是为什么苹果建议:如果没有自定义绘制的任务就不要在子类中写一个空的-drawRect:方法。)
方法二:CALayer有一个可选的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);
}
我们在blueLayer上显式地调用了-display。不同于UIView,当图层显示在屏幕上时,CALayer不会自动重绘它的内容。它把重绘的决定权交给了开发者。
尽管我们没有用masksToBounds属性,绘制的那个圆仍然沿边界被裁剪了。这是因为当你使用CALayerDelegate绘制寄宿图的时候,并没有对超出边界外的内容提供绘制支持。
9.布局:
frame并不是一个非常清晰的属性,它其实是一个虚拟属性,是根据bounds,position和transform计算而来,所以当其中任何一个值发生改变,frame都会变化。相反,改变frame的值同样会影响到他们当中的值;
锚点:
10.坐标系:
- (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,就可以把图层向相机方向前置,于是它就在所有其他图层的前面了(或者至少是小于它的zPosition值的图层的前面);同样适用于视图的layer
12.Hit Testing
CALayer并不关心任何响应链事件,所以不能直接处理触摸事件或者手势。但是它有一系列的方法帮你处理事件:-containsPoint:和-hitTest:。
13.自动布局
如果想随意控制CALayer的布局,就需要手工操作。最简单的方法就是使用CALayerDelegate如下函数:
- (void)layoutSublayersOfLayer:(CALayer *)layer;
当图层的bounds发生改变,或者图层的-setNeedsLayout方法被调用的时候,这个函数将会被执行。这使得你可以手动地重新摆放或者重新调 整子图层的大小,但是不能像UIView的autoresizingMask和constraints属性做到自适应屏幕旋转。
14.conrnerRadius
默认情况下,这个曲率值只影响背景颜色而不影响背景图片或是子图层。不过,如果把masksToBounds设置成YES的话,图层里面的所有东西都会被截取
15.shadowOpacity
若要改动阴影的表现,你可以使用CALayer的另外三个属性:shadowColor,shadowOffset和shadowRadius
16.shadowPath属性
我们已经知道图层阴影并不总是方的,而是从图层内容的形状继承而来。这看上去不错,但是实时计算阴影也是一个非常消耗资源的,尤其是图层有多个子图层,每个图层还有一个有透明效果的寄宿图的时候。
如果你事先知道你的阴影形状会是什么样子的,你可以通过指定一个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蒙板图层真正厉害的地方在于蒙板图不局限于静态图。任何有图层构成的都可以作为mask属性,这意味着你的蒙板可以通过代码甚至是动画实时生成。
18.shouldRasterize
实现组透明的效果,如果它被设置为YES,在应用透明度之前,图层及其子图层都会被整合成一个整体的图片,这样就没有透明度混合的问题了;
为了启用shouldRasterize属性,我们设置了图层的rasterizationScale属性。默认情况下,所有图层拉伸都是1.0, 所以如果你使用了shouldRasterize属性,你就要确保你设置了rasterizationScale属性去匹配屏幕,以防止出现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;
设置CGAffineTransform的矩阵值,做任意变换:
CGAffineTransform transform =CGAffineTransformIdentity;
transform.c= -x;
transform.b= y;
20.变换(CATransform3D)
对应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---->应用透视效果
21.sublayerTransform
CALayer有一个属性叫做sublayerTransform。它也是CATransform3D类型,但和对一个图层的变换不同,它影响到所有的子图层。这意味着你可以一次性对包含这些图层的容器做变换,于是所有的子图层都自动继承了这个变换方法。
22.doubleSided
CALayer有一个叫做doubleSided的属性来控制图层的背面是否要被绘制。这是一个BOOL类型,默认为YES,如果设置为NO,那么当图层正面从相机视角消失的时候,它将不会被绘制。
23.CAShapeLayer
使用CAShapeLayer有以下一些优点:
渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉(如我们在第二章所见)。
不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
24.CATextLayer
CATextLayer比UILabel有着更好的性能表现,同时还有额外的布局选项并且在iOS 5上支持富文本。
让我们编辑一下示例使用到NSAttributedString(见清单6.3).iOS 6及以上我们可以用新的NSTextAttributeName实例来设置我们的字符串属性,但是练习的目的是为了演示在iOS 5及以下,所以我们用了Core Text,也就是说你需要把Core Text framework添加到你的项目中。否则,编译器是无法识别属性常量的。
UILabel的替代品
每一个UIView都是寄宿在一个CALayer的示例上。这个图层是由视图自动创建和管理的,那我们可以用别的图层类型替代它么?一旦被创建,我们就无法代替这个图层了。但是如果我们继承了UIView,那我们就可以重写+layerClass方法使得在创建的时候能返回一个不同的图层子类。UIView会在初始化的时候调用+layerClass方法,然后用它的返回类型来创建宿主图层。
把CATextLayer作为宿主图层的另一好处就是视图自动设置了contentsScale属性。
25.CATransformLayer
在立方体示例,我们将通过旋转camara来解决图层平面化问题而不是像立方体示例代码中用的sublayerTransform。这是一个非常不错的技巧,但是只能作用域单个对象上,如果你的场景包含两个立方体,那我们就不能用这个技巧单独旋转他们了。
CATransformLayer不同于普通的CALayer,因为它不能显示它自己的内容。只有当存在了一个能作用域子图层的变换它才真正存在。CATransformLayer并不平面化它的子图层,所以它能够用于构造一个层级的3D结构。
26.CAGradientLayer
CAGradientLayer是用来生成两种或更多颜色平滑渐变的。用Core Graphics复制一个CAGradientLayer并将内容绘制到一个普通图层的寄宿图也是有可能的,但是CAGradientLayer的真正好处在于绘制使用了硬件加速。
27.CAReplicatorLayer
CAReplicatorLayer的目的是为了高效生成许多相似的图层。它会绘制一个或多个图层的子图层,并在每个复制体上应用不同的变换。
instanceCount属性指定了图层需要重复多少次。instanceTransform指定了一个CATransform3D 3D变换(这种情况下,下一图层的位移和旋转将会移动到圆圈的下一个点)。变换是逐步增加的,每个实例都是相对于前一实例布局。这就是为什么这些复制体最终不会出现在同一位置上;
CAReplicatorLayer真正应用到实际程序上的场景比如:一个游戏中导弹的轨迹云,或者粒子爆炸。除此之外,还有一个实际应用是:反射。
28.CAScrollLayer
CAScrollLayer有一个-scrollToPoint:方法,它自动适应bounds的原点以便图层内容出现在滑动的地方。注意,这就是它做的所有事情。前面提到过,Core Animation并不处理用户输入,所以CAScrollLayer并不负责将触摸事件转换为滑动事件,既不渲染滚动条,也不实现任何iOS指定行为例如滑动反弹(当视图滑动超多了它的边界的将会反弹回正确的地方)。
1.CATransaction
事务;
UIView有两个方法,+beginAnimations:context:和+commitAnimations,和CATransaction的+begin 和+commit方法类似。实际上在+beginAnimations:context:和+commitAnimations之间所有视图或者图层属性的改变而做的动画都是由于设置了CATransaction的原因。
UIView封装动画中的块动画,对做一堆的属性动画在语法上会更加简单,但实质上它们都是在做同样的事情。
基于UIView的block的动画允许你在动画结束的时候提供一个完成的动作。CATranscation接口提供的+setCompletionBlock:方法也有同样的功能
2.属性动画实质:
当CALayer的属性被修改时候,它会调用-actionForKey:方法,传递属性的名称。剩下的操作都在CALayer的头文件中有详细的说明,实质上是如下几步:
图层首先检测它是否有委托,并且是否实现CALayerDelegate协议指定的-actionForLayer:forKey方法。如果有,直接调用并返回结果。
如果没有委托,或者委托没有实现-actionForLayer:forKey方法,图层接着检查包含属性名称对应行为映射的actions字典。
如果actions字典没有包含对应的属性,那么图层接着在它的style字典接着搜索属性名。
最后,如果在style里面也找不到对应的行为,那么图层将会直接调用定义了每个属性的标准行为的-defaultActionForKey:方法。
所以一轮完整的搜索结束之后,-actionForKey:要么返回空(这种情况下将不会有动画发生),要么是CAAction协议对应的对象,最后CALayer拿这个结果去对先前和当前的值做动画。
通过下面一段代码,可以理解每个UIView对它关联的图层是如何禁用隐式动画的
- (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];
}
运行结果:
2016-05-3014:48:18.614测试[10222:147396] Outside: 2016-05-3014:48:18.615测试[10222:147396] Inside:
总结一下,我们知道了如下几点
UIView 关联的图层禁用了隐式动画,对这种图层做动画的唯一办法就是使用UIView的动画函数(而不是依赖CATransaction),或者继承 UIView,并覆盖-actionForLayer:forKey:方法,或者直接创建一个显式动画(具体细节见第八章)。
对于单独存在的图层,我们可以通过实现图层的-actionForLayer:forKey:委托方法,或者提供一个actions字典来控制隐式动画。
3.CATransition
CATransition继承CAAnimation,而CAAnimation响应CAAction协议;
CATransition *transition =[CATransition animation];
transition.type=kCATransitionPush;
transition.subtype=kCATransitionFromLeft;
self.colorLayer.actions= @{@"backgroundColor": transition};
这种方法,只是针对单独的图层,不能用在UIView关联的图层;
4.presentationLayer
每个图层属性的显示值都被存储在一个叫做呈现图层的独立图层当中,他可以通过-presentationLayer方法来访问。这个呈现图层实际上是模型图层的复制,但是它的属性值代表了在任何指定时刻当前外观效果。换句话说,你可以通过呈现图层的值来获取当前屏幕上真正显示出来的值
注意呈现图层仅仅当图层首次被提交(就是首次第一次在屏幕上显示)的时候创建,所以在那之前调用-presentationLayer将会返回nil。
在呈现图层上调用–modelLayer将会返回它正在呈现所依赖的CALayer。通常在一个图层上调用-modelLayer会返回–self(实际上我们已经创建的原始图层就是一种数据模型)。
5.显式动画
在iOS中核心动画分为几类:基础动画、关键帧动画、动画组、转场动画。各个类的关系大致如下:
CAAnimation:核心动画的基础类,不能直接使用,负责动画运行时间、速度的控制。CAAnimation本身并没有做多少工作,它提供了一个计时函数,一个委托(用于反馈动画状态)以及一个 removedOnCompletion,用于标识动画是否该在结束后自动释放(默认YES,为了防止内存泄露)。CAAnimation同时实现了一些 协议,包括CAAction(允许CAAnimation的子类可以提供图层行为),以及CAMediaTiming
CAPropertyAnimation:属性动画的基类(通过属性进行动画设置,注意是可动画属性),不能直接使用。通过指定动画的keyPath作用于一个单一属性,CAAnimation通常应用于一个指定的CALayer,于是这里指的也就是一个图层的 keyPath了。实际上它是一个关键路径(一些用点表示法可以在层级关系中指向任意嵌套的对象),而不仅仅是一个属性的名称,因为这意味着动画不仅可以 作用于图层本身的属性,而且还包含了它的子成员的属性,甚至是一些虚拟的属性
CAAnimationGroup:动画组,动画组是一种组合模式设计,可以通过动画组来进行所有动画行为的统一控制,组中所有动画效果可以并发执行。
CATransition:过渡(转场)动画,主要通过滤镜进行动画效果设置。过渡并不像属性动画那样平滑地在两个值之间做动画,而是影响到整个图层的变化。过渡动画首先展示之前的图层外观,然后通过一个交换过渡到新的外观。
CABasicAnimation:基础动画,通过属性修改进行动画参数控制,只有初始状态和结束状态。
CAKeyframeAnimation:关键帧动画,同样是通过属性进行动画参数控制,但是同基础动画不同的是它可以有多个状态控制。CAKeyFrameAnimation添加了一个rotationMode的属性。设置它为常量kCAAnimationRotateAuto,图层将会根据曲线的切线自动旋转
基础动画、关键帧动画都属于属性动画,就是通过修改属性值产生动画效果,开发人员只需要设置初始值和结束值,中间的过程动画(又叫“补间动画”)由 系统自动计算产生。和基础动画不同的是关键帧动画可以设置多个属性值,每两个属性中间的补间动画由系统自动完成,因此从这个角度而言基础动画又可以看成是 有两个关键帧的关键帧动画。
- (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做动画的好处如下:
我们可以不通过关键帧一步旋转多于180度的动画。
可以用相对值而不是绝对值旋转(设置byValue而不是toValue)。
可以不用创建CATransform3D,而是使用一个简单的数值来指定角度。
不会和transform.position或者transform.scale冲突(同样是使用关键路径来做独立的动画属性)。
transform.rotation 属性有一个奇怪的问题是它其实并不存在。这是因为CATransform3D并不是一个对象,它实际上是一个结构体,也没有符合KVC相关属 性,transform.rotation实际上是一个CALayer用于处理动画变换的虚拟属性。
7.动画组
//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.过渡动画
CAAnimation有一个type和subtype来标识变换效果。type属性是一个NSString类型,可以被设置成如下类型:
kCATransitionFade
kCATransitionMoveIn
kCATransitionPush
kCATransitionReveal
通过subtype来控制它们的方向,提供了如下四种类型:
kCATransitionFromRight
kCATransitionFromLeft
kCATransitionFromTop
kCATransitionFromBottom
隐式过渡
CATransision 可以对图层任何变化平滑过渡的事实使得它成为那些不好做动画的属性图层行为的理想候选。苹果当然意识到了这点,并且当设置了CALayer的 content属性的时候,CATransition的确是默认的行为。但是对于视图关联的图层,或者是其他隐式动画的行为,这个特性依然是被禁用的,但 是对于你自己创建的图层,这意味着对图层contents图片做的改动都会自动附上淡入淡出的动画。
对图层树的动画
CATransition并不作用于指定的图层属性,这就是说你可以在即使不能准确得知改变了什么的情况下对图层做动画,例如,在不知道 UITableView哪一行被添加或者删除的情况下,直接就可以平滑地刷新它,或者在不知道UIViewController内部的视图层级的情况下对 两个不同的实例做过渡动画。
这些例子和我们之前所讨论的情况完全不同,因为它们不涉及到图层的属性,而且是整个图层树的改变--我们在这种动画的过程中手动在层级关系中添加或者移除图层。
这里用到了一个小诡计,要确保CATransition添加到的图层在过渡动画发生时不会在树状结构中被移除,否则CATransition将会和图层一起被移除。一般来说,你只需要将动画添加到被影响图层的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];
}
自定义动画
奇怪的是苹果通过UIView +transitionFromView:toView:duration:options:completion: 和+transitionWithView:duration:options:animations:方法提供了Core Animation的过渡特性。但是这里的可用的过渡选项和CATransition的type属性提供的常量完全不同。UIView过渡方法中 options参数可以由如下常量指定:
UIViewAnimationOptionTransitionFlipFromLeft
UIViewAnimationOptionTransitionFlipFromRight
UIViewAnimationOptionTransitionCurlUp
UIViewAnimationOptionTransitionCurlDown
UIViewAnimationOptionTransitionCrossDissolve
UIViewAnimationOptionTransitionFlipFromTop
UIViewAnimationOptionTransitionFlipFromBottom
过渡动画做基础的原则就是对原始的图层外观截图,然后添加一段动画,平滑过渡到图层改变之后那个截图的效果。如果我们知道如何对图层截图,我们就可以使用属性动画来代替CATransition或者是UIKit的过渡方法来实现动画。
事实证明,对图层做截图还是很简单的。CALayer有一个-renderInContext:方法,可以通过把它绘制到Core Graphics的上下文中捕获当前内容的图片,然后在另外的视图中显示出来。如果我们把这个截屏视图置于原始视图之上,就可以遮住真实视图的所有变化, 于是重新创建了一个简单的过渡效果。
清单8.14演示了一个基本的实现。我们对当前视图状态截图,然后在我们改变原始视图的背景色的时候对截图快速转动并且淡出,图8.5展示了我们自定义的过渡效果。
为 了让事情更简单,我们用UIView -animateWithDuration:completion:方法来实现。虽然用CABasicAnimation可以达到同样的效果,但是那样的 话我们就需要对图层的变换和不透明属性创建单独的动画,然后当动画结束的是哦户在CAAnimationDelegate中把coverView从屏幕中 移除。
清单8.14 用renderInContext:创建自定义过渡效果
-(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协议
duration
repeatCount
repeatDuration
autoreverses
注意repeatCount和repeatDuration可能会相互冲突,所以你只要对其中一个指定非零值。对两个属性都设置非0值的行为没有被定义。
beginTime
指定了动画开始之前的的延迟时间。这里的延迟从动画添加到可见图层的那一刻开始测量,默认是0(就是说动画会立刻执行)。
speed
是一个时间的倍数,默认1.0,减少它会减慢图层/动画的时间,增加它会加快速度。如果2.0的速度,那么对于一个duration为1的动画,实际上在0.5秒的时候就已经完成了。
timeOffset
和beginTime类似,但是和增加beginTime导致的延迟动画不同,增加timeOffset只是让动画快进到某一点,例如,对于一个持续1秒的动画来说,设置timeOffset为0.5意味着动画将从一半的地方开始。
fillMode
fillMode是一个NSString类型,可以接受如下四种常量:
kCAFillModeForwards
kCAFillModeBackwards
kCAFillModeBoth
kCAFillModeRemoved
默认是kCAFillModeRemoved,当动画不再播放的时候就显示图层模型指定的值剩下的三种类型向前,向后或者即向前又向后去填充动画状态,使得动画在开始前或者结束后仍然保持开始和结束那一刻的值。
这就对避免在动画结束的时候急速返回提供另一种方案(见第八章)。但是记住了,当用它来解决这个问题的时候,需要把removeOnCompletion设置为NO,另外需要给动画添加一个非空的键,于是可以在不需要动画的时候把它从图层上移除。
暂停,倒回和快进(图层的speed)
设置动画的speed属性为0可以暂停动画,但在动画被添加到图层之后不太可能再修改它了,所以不能对正在进行的动画使用这个属性。给图层添加一个 CAAnimation实际上是给动画对象做了一个不可改变的拷贝,所以对原始动画对象属性的改变对真实的动画并没有作用。相反,直接用 -animationForKey:来检索图层正在进行的动画可以返回正确的动画对象,但是修改它的属性将会抛出异常。
如果移除图层正在进行的动画,图层将会急速返回动画之前的状态。但如果在动画移除之前拷贝呈现图层到模型图层,动画将会看起来暂停在那里。但是不好的地方在于之后就不能再恢复动画了。
一个简单的方法是可以利用CAMediaTiming来暂停图层本身。如果把图层的speed设置成0,它会暂停任何添加到图层上的动画。类似的,设置speed大于1.0将会快进,设置成一个负值将会倒回动画。
通过增加主窗口图层的speed,可以暂停整个应用程序的动画。这对UI自动化提供了好处,我们可以加速所有的视图动画来进行自动化测试(注意对于在主窗口之外的视图并不会被影响,比如UIAlertview)。可以在app delegate设置如下进行验证:
self.window.layer.speed =100;
你也可以通过这种方式来减速,但其实也可以在模拟器通过切换慢速动画来实现。
手动动画(图层的timeOffset)
timeOffset一个很有用的功能在于你可以它可以让你手动控制动画进程,通过设置speed为0,可以禁用动画的自动播放,然后来使用timeOffset来来回显示动画序列。这可以使得运用手势来手动控制动画变得很简单。
@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
动画和屏幕上组合的图层实际上被一个单独的进程管理,而不是你的应用程序。这个进程就是所谓的渲染服务。在iOS5和之前的版本是SpringBoard进程(同时管理着iOS的主屏)。在iOS6之后的版本中叫做BackBoard。
一件重要的事情就是性能测试一定要用发布配置,而不是调试模式。因为当用发布环境打包的时候,编译器会引入一系列提高性能的优化,例如去掉调试符号或者移 除并重新组织代码。你也可以自己做到这些,例如在发布环境禁用NSLog语句。你只关心发布性能,那才是你需要测试的点。
Core Animation工具也提供了一系列复选框选项来帮助调试渲染瓶颈:
Color Blended Layers - 这个选项基于渲染程度对屏幕中的混合区域进行绿到红的高亮(也就是多个半透明图层的叠加)。由于重绘的原因,混合对GPU性能会有影响,同时也是滑动或者动画帧率下降的罪魁祸首之一。
ColorHitsGreenandMissesRed - 当使用shouldRasterizep属性的时候,耗时的图层绘制会被缓存,然后当做一个简单的扁平图片呈现。当缓存再生的时候这个选项就用红色对栅格 化图层进行了高亮。如果缓存频繁再生的话,就意味着栅格化可能会有负面的性能影响了(更多关于使用shouldRasterize的细节见第15章“图层 性能”)。
Color Copied Images - 有时候寄宿图片的生成意味着Core Animation被强制生成一些图片,然后发送到渲染服务器,而不是简单的指向原始指针。这个选项把这些图片渲染成蓝色。复制图片对内存和CPU使用来 说都是一项非常昂贵的操作,所以应该尽可能的避免。
Color Immediately - 通常Core Animation Instruments以每毫秒10次的频率更新图层调试颜色。对某些效果来说,这显然太慢了。这个选项就可以用来设置每帧都更新(可能会影响到渲染性 能,而且会导致帧率测量不准,所以不要一直都设置它)。
Color Misaligned Images - 这里会高亮那些被缩放或者拉伸以及没有正确对齐到像素边界的图片(也就是非整型坐标)。这些中的大多数通常都会导致图片的不正常缩放,如果把一张大图当缩 略图显示,或者不正确地模糊图像,那么这个选项将会帮你识别出问题所在。
Color Offscreen-Rendered Yellow - 这里会把那些需要离屏渲染的图层高亮成黄色。这些图层很可能需要用shadowPath或者shouldRasterize来优化。
Color OpenGL Fast Path Blue - 这个选项会对任何直接使用OpenGL绘制的图层进行高亮。如果仅仅使用UIKit或者Core Animation的API,那么不会有任何效果。如果使用GLKView或者CAEAGLLayer,那如果不显示蓝色块的话就意味着你正在强制CPU 渲染额外的纹理,而不是绘制到屏幕。
Flash Updated Regions - 这个选项会对重绘的内容高亮成黄色(也就是任何在软件层面使用Core Graphics绘制的图层)。这种绘图的速度很慢。如果频繁发生这种情况的话,这意味着有一个隐藏的bug或者说通过增加缓存或者使用替代方案会有提升 性能的空间。