Features
contents属性
CALayer有一个属性叫做contents,这个属性的类型被定义为id,意味着它可以是任何类型的对象。在这种情况下,你可以给contents属性赋任何值,你的app仍然能够编译通过。但是,在实践中,如果你给contents赋的不是CGImage,那么你得到的图层将是空白的。
所以基本的使用情况:
layer.contents = (__bridge id)image.CGImage;
不过貌似本地测试的时候只是强转了id也能生效
contentsGravity
同UIImageView一样,在大小不合适的情况下,会出现照片的适应性问题。解决方法就是把contentMode属性设置成更合适的值,像这样:
view.contentMode = UIViewContentModeScaleAspectFit;
这个方法基本和我们遇到的情况的解决方法已经接近了(你可以试一下 :) ),不过UIView大多数视觉相关的属性比如contentMode,对这些属性的操作其实是对对应图层的操作。
CALayer与contentMode对应的属性叫做contentsGravity,但是它是一个NSString类型,而不是像对应的UIKit部分,那里面的值是枚举。
和cotentMode一样,contentsGravity的目的是为了决定内容在图层的边界中怎么对齐,我们将使用kCAGravityResizeAspect,它的效果等同于UIViewContentModeScaleAspectFit, 同时它还能在图层中等比例拉伸以适应图层的边界。
self.layerView.layer.contentsGravity = kCAGravityResizeAspect;
maskToBounds
默认情况下,UIView仍然会绘制超过边界的内容或是子视图,在CALayer下也是这样的。 UIView有一个叫做clipsToBounds的属性可以用来决定是否显示超出边界的内容,CALayer对应的属性叫做masksToBounds,把它设置为YES,图片就在边界里啦~
还有一些其他属性,诸如 contentsScale
、contentsRect
、contentsCenter
、等觉得是并不常用,暂不做介绍了。
CAShapeLayer
CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类。你指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图形,最后CAShapeLayer就自动渲染出来了。当然,你也可以用Core Graphics直接向原始的CALyer的内容中绘制一个路径,相比直下,使用CAShapeLayer有以下一些优点:
- 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
- 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
- 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉。
- 不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
创建一个CGPath
CAShapeLayer可以用来绘制所有能够通过CGPath来表示的形状。这个形状不一定要闭合,图层路径也不一定要不可破,事实上你可以在一个图层上绘制好几个不同的形状。你可以控制一些属性比如lineWith
(线宽,用点表示单位),lineCap
(线条结尾的样子),和lineJoin
(线条之间的结合点的样子);但是在图层层面你只有一次机会设置这些属性。如果你想用不同颜色或风格来绘制多个形状,就不得不为每个形状准备一个图层了。
下面的代码用一个CAShapeLayer渲染一个简单的火柴人。CAShapeLayer属性是CGPathRef类型,但是我们用UIBezierPath帮助类创建了图层路径,这样我们就不用考虑人工释放CGPath了。
UIView *containerView = [[UIView alloc] initWithFrame:self.view.bounds]; containerView.backgroundColor = [UIColor lightGrayColor]; [self.view addSubview:containerView]; // create path UIBezierPath *path = [[UIBezierPath alloc] init]; [path moveToPoint:CGPointMake(175, 100)]; [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle:2*M_PI clockwise:YES]; [path moveToPoint:CGPointMake(150, 125)]; [path addLineToPoint:CGPointMake(150, 175)]; [path addLineToPoint:CGPointMake(125, 225)]; [path moveToPoint:CGPointMake(150, 175)]; [path addLineToPoint:CGPointMake(175, 225)]; [path moveToPoint:CGPointMake(100, 150)]; [path addLineToPoint:CGPointMake(200, 150)]; // create shapr layer CAShapeLayer *layer = [CAShapeLayer layer]; layer.strokeColor = [UIColor redColor].CGColor; layer.fillColor = [UIColor clearColor].CGColor; layer.lineWidth = 5; layer.lineJoin = kCALineJoinRound; layer.lineCap = kCALineCapRound; layer.path = path.CGPath; // add layer to view [containerView.layer addSublayer:layer];
圆角
CAShapeLayer为创建圆角视图提供了一个方法,就是CALayer的cornerRadius属性。虽然使用CAShapeLayer类需要更多的工作,但是它有一个优势就是可以单独指定每个角。
我们创建圆角矩形其实就是人工绘制单独的直线和弧度,但是事实上UIBezierPath有自动绘制圆角矩形的构造方法,下面这段代码绘制了一个有三个圆角一个直角的矩形:
// define path parameters CGRect rect = CGRectMake(0, 0, 100, 100); UIRectCorner corners = UIRectCornerTopRight | UIRectCornerBottomRight | UIRectCornerBottomLeft; CGSize radii = CGSizeMake(30, 30); // create path UIBezierPath *corPath = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:radii]; CAShapeLayer *cornerLayer = [CAShapeLayer layer]; cornerLayer.frame = CGRectMake(100, 260, 100, 100); cornerLayer.path = corPath.CGPath; cornerLayer.fillColor = [UIColor blueColor].CGColor; [containerView.layer addSublayer:cornerLayer];
我们可以通过这个图层路径绘制一个既有直角又有圆角的视图。如果我们想依照此图形来剪裁视图内容,我们可以把CAShapeLayer作为视图的宿主图层,而不是添加一个子视图。
CAEmitterLayer
在iOS 5中,苹果引入了一个新的CALayer子类叫做CAEmitterLayer。CAEmitterLayer是一个高性能的粒子引擎,被用来创建实时例子动画如:烟雾,火,雨等等这些效果。
CAEmitterLayer看上去像是许多CAEmitterCell的容器,这些CAEmitierCell定义了一个例子效果。你将会为不同的例子效果定义一个或多个CAEmitterCell作为模版,同时CAEmitterLayer负责基于这些模版实例化一个粒子流。一个CAEmitterCell类似于一个CALayer:它有一个contents属性可以定义为一个CGImage,另外还有一些可设置属性控制着表现和行为。我们不会对这些属性逐一进行详细的描述,你们可以在CAEmitterCell类的头文件中找到。
类似于facebook的点赞效果:(这里就不加放大缩小的效果了)
代码如下:
CAEmitterCell *explosionCell = [CAEmitterCell emitterCell]; // The name of the cell,用于构建key paths。这也是后面手动控制动画开始和结束的关键。 explosionCell.name = @"explosion"; explosionCell.alphaRange = 0.10; explosionCell.alphaSpeed = -1.0; // 下面两个属性如果只用了lifetime那么粒子的存活时间就是固定的,比如lifetime=10,那么粒子10s秒后就消失了。 // 如果使用了lifetimeRange,比如lifetimeRange=5,那么粒子的存活时间就是在5s~15s这个范围内消失。 explosionCell.lifetime = 0.7; // 粒子存活的时间,以秒为单位 explosionCell.lifetimeRange = 0.3; // 可以为这个粒子存活的时间再指定一个范围 explosionCell.birthRate = 0; // 每秒生成多少个粒子 // 粒子平均初始速度。正数表示竖直向上,负数竖直向下 explosionCell.velocity = 40.00; // 可以再指定一个范围 explosionCell.velocityRange = 10.00; explosionCell.scale = 0.03; explosionCell.scaleRange = 0.02; explosionCell.contents = (id)[UIImage imageNamed:@"Sparkle"].CGImage; self.explosionLayer = [CAEmitterLayer layer]; self.explosionLayer.name = @"emitterLayer"; // 发射源的形状 self.explosionLayer.emitterShape = kCAEmitterLayerCircle; // 发射源的发射模式 self.explosionLayer.emitterMode = kCAEmitterLayerOutline; //发射源大小。注意除了宽和高之外,还有纵向深度emitterDepth self.explosionLayer.emitterSize = CGSizeMake(10, 0); self.explosionLayer.emitterCells = @[explosionCell]; self.explosionLayer.renderMode = kCAEmitterLayerOldestFirst; self.explosionLayer.masksToBounds = NO; self.explosionLayer.position = CGPointMake(10, 10); // 发射源位置 self.explosionLayer.zPosition = -1; // 发射源位置 [self.button.layer addSublayer:self.explosionLayer]; [self.button addTarget:self action:@selector(startC) forControlEvents:UIControlEventTouchUpInside];
- (void)startC { //进入下一个动作 // [self performSelector:@selector(explode) withObject:nil afterDelay:0.2]; // //explosionLayer开始时间 self.explosionLayer.beginTime = CACurrentMediaTime(); //explosionLayer每秒喷射的2500个 //CAEmitterLayer 根据自己的 emitterCells 属性找到名叫 explosion 的 cell, //并设置它的 birthRate 为 500。从而间接地控制了动画的开始。 [self.explosionLayer setValue:@2500 forKeyPath:@"emitterCells.explosion.birthRate"]; //停止喷射 [self performSelector:@selector(stop) withObject:nil afterDelay:0.1]; }
/** * 大量喷射 */ - (void)explode { //explosionLayer开始时间 self.explosionLayer.beginTime = CACurrentMediaTime(); //explosionLayer每秒喷射的2500个 [self.explosionLayer setValue:@2500 forKeyPath:@"emitterCells.explosion.birthRate"]; //停止喷射 [self performSelector:@selector(stop) withObject:nil afterDelay:0.1]; } /** * 停止喷射 */ - (void)stop { [self.explosionLayer setValue:@0 forKeyPath:@"emitterCells.explosion.birthRate"]; }
emitterShape
,发射源的形状,平常用的多的比如 emitterShape 的 kCAEmitterLayerLine 和 kCAEmitterLayerPoint。这两个从视觉上还是比较好区分的,这决定了你的粒子是从一个点「喷」出来的,还是从一条线上每个点「喷」下来,前者像焰火,后者像瀑布。显然,下雪的效果更像后者。emitterMode
的 kCAEmitterLayerOutline 表示向外围扩散,如果你的发射源形状是 circle,那么 kCAEmitterLayerOutline 就会以一个圆的方式向外扩散开。又比如你想表达一股蒸汽向上喷的效果,就可以设置 emitterShape 为 kCAEmitterLayerLine , emitterMode 为 kCAEmitterLayerOutline。
CAEmitterLayer的属性它自己控制着整个例子系统的位置和形状。一些属性比如birthRate,lifetime和celocity,这些属性在CAEmitterCell中也有。这些属性会以相乘的方式作用在一起,这样你就可以用一个值来加速或者扩大整个例子系统。其他值得提到的属性有以下这些:
preservesDepth
,是否将3D例子系统平面化到一个图层(默认值)或者可以在3D空间中混合其他的图层renderMode
,控制着在视觉上粒子图片是如何混合的。
下雪的效果
代码如下:
CAEmitterLayer *snowEmitter = [CAEmitterLayer layer]; //发射点的位置 snowEmitter.emitterPosition = CGPointMake(CGRectGetWidth(self.view.frame) / 2, -30); snowEmitter.emitterSize = CGSizeMake(CGRectGetWidth(self.view.frame) * 2, 0.0f); snowEmitter.emitterShape = kCAEmitterLayerLine; // 发射源的形状 snowEmitter.emitterMode = kCAEmitterLayerOutline; snowEmitter.shadowColor = [UIColor whiteColor].CGColor; snowEmitter.shadowOffset = CGSizeMake(0.0f, 1.0f); snowEmitter.shadowRadius = 0.0f; snowEmitter.shadowOpacity = 1.0f; CAEmitterCell *snowCell = [CAEmitterCell emitterCell]; snowCell.birthRate = 1.0f; //每秒出现多少个粒子 snowCell.lifetime = 120.0f; // 粒子的存活时间 snowCell.velocity = -10; //速度 snowCell.velocityRange = 10; // 平均速度 snowCell.yAcceleration = 2;//粒子在y方向上的加速度 snowCell.emissionRange = 0.5f * M_PI; //发射的弧度 snowCell.spinRange = 0.75f * M_PI; // 粒子的平均旋转速度 snowCell.contents = (id)[UIImage imageNamed:@"snow"].CGImage; snowCell.color = [UIColor colorWithRed:0.6 green:0.658 blue:0.743 alpha:1.0].CGColor; snowEmitter.emitterCells = @[snowCell]; [self.view.layer insertSublayer:snowEmitter atIndex:0];
有时候返回的时候,layer还未移除,所以会看到部分影像,可以在返回的时候手动移除:
for (CALayer *subLayer in self.view.layer.sublayers) { if ([subLayer isKindOfClass:[CAEmitterLayer class]]) { CAEmitterLayer *la = (CAEmitterLayer *)subLayer; [la removeFromSuperlayer]; } }
CAGradientLayer
CAGradientLayer是用来生成两种或更多颜色平滑渐变的。用Core Graphics复制一个CAGradientLayer并将内容绘制到一个普通图层的寄宿图也是有可能的,但是CAGradientLayer的真正好处在于绘制使用了硬件加速。
基础渐变
从一个简单的红变蓝的对角线渐变开始。
这些渐变色彩放在一个数组中,并赋给colors属性。这个数组成员接受CGColorRef类型的值(并不是从NSObject派生而来),所以我们要用通过bridge转换以确保编译正常。
CAGradientLayer也有startPoint和endPoint属性,他们决定了渐变的方向。这两个参数是以单位坐标系进行的定义,所以左上角坐标是{0, 0},右下角坐标是{1, 1}。代码如下:
/* * 简单的两种颜色的对角线渐变 * */ self.contentView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; [self.view addSubview:self.contentView]; CAGradientLayer *gradientLayer = [CAGradientLayer layer]; gradientLayer.frame = self.contentView.bounds; [self.contentView.layer addSublayer:gradientLayer]; // set gradient colors gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blueColor].CGColor]; // 单位坐标系进行的定义,所以左上角坐标是{0, 0},右下角坐标是{1, 1} gradientLayer.startPoint = CGPointMake(0, 0); gradientLayer.endPoint = CGPointMake(1, 1);
对应的效果:
多重渐变
如果你愿意,colors属性可以包含很多颜色,所以创建一个彩虹一样的多重渐变也是很简单的。默认情况下,这些颜色在空间上均匀地被渲染,但是我们可以用locations
属性来调整空间。locations属性是一个浮点数值的数组(以NSNumber包装)。这些浮点数定义了colors属性中每个不同颜色的位置,同样的,也是以单位坐标系进行标定。0.0代表着渐变的开始,1.0代表着结束。
locations数组并不是强制要求的,但是如果你给它赋值了就一定要确保locations的数组大小和colors数组大小一定要相同,否则你将会得到一个空白的渐变。
重新构造一个渐变的形式:
/* * 多重渐变 * */ self.contentView2 = [[UIView alloc] initWithFrame:CGRectMake(100, 300, 100, 100)]; [self.view addSubview:self.contentView2]; CAGradientLayer *gradientLayer2 = [CAGradientLayer layer]; gradientLayer2.frame = self.contentView2.bounds; [self.contentView2.layer addSublayer:gradientLayer2]; // set gradient colors gradientLayer2.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor yellowColor].CGColor, (__bridge id)[UIColor greenColor].CGColor]; // set locations gradientLayer2.locations = @[@0.0, @0.25, @0.5]; //set gradient start and end points gradientLayer2.startPoint = CGPointMake(0, 0); gradientLayer2.endPoint = CGPointMake(1, 1);
类似于iPhone上面的解锁提示文案, > 滑动来解锁
,就使用这种方式来实现。
FB有个开源库,就是来做这种事的: facebook/Shimmer ٩(˃̶͈̀௰˂̶͈́)و
CAReplicatorLayer
CAReplicatorLayer的目的是为了高效生成许多相似的图层。它会绘制一个或多个图层的子图层
,并在每个复制体上应用不同的变换。
先看一下基本的动画效果,包括心形和音律跳动的动画。
重复图层之音律跳动
我们先创建一个CAReplicatorLayer,并在上面添加子图层,给该子图层增加一个动画效果。之后设置复制的个数、间隔以及动画的延迟时候等,做出动画效果。
为了有个更好的参考,添加了个参考的图层。
#define SYS_DEVICE_WIDTH ([[UIScreen mainScreen] bounds].size.width) // 屏幕宽度 #define SYS_DEVICE_HEIGHT ([[UIScreen mainScreen] bounds].size.height) // 屏幕长度 CAReplicatorLayer *_musicLayer = [CAReplicatorLayer layer]; _musicLayer.frame = CGRectMake(SYS_DEVICE_WIDTH/2.0-50, 410, 100, 100); _musicLayer.backgroundColor = [UIColor greenColor].CGColor; _musicLayer.masksToBounds = YES; [self.view.layer addSublayer:_musicLayer]; // 创建一个子layer,并以此来作为复制的基础 CALayer *tLayer = [CALayer layer]; tLayer.backgroundColor = [UIColor redColor].CGColor; [_musicLayer addSublayer:tLayer]; // 给个参考的图层 #if 0 CALayer *ttLayer = [CALayer layer]; ttLayer.backgroundColor = [UIColor blueColor].CGColor; [_musicLayer addSublayer:ttLayer]; ttLayer.frame = CGRectMake(20, 70, 10, 40); #endif // 给该子layer加动画 CABasicAnimation *musicAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"]; musicAnimation.duration = 0.35; musicAnimation.autoreverses = YES; musicAnimation.repeatCount = MAXFLOAT; // 通过改变位置关系来展现不同的动画效果 #if 0 tLayer.frame = CGRectMake(10, 70, 10, 30); musicAnimation.fromValue = @(70); musicAnimation.toValue = @(45); #else tLayer.frame = CGRectMake(10, 100, 10, 30); musicAnimation.fromValue = @(100); musicAnimation.toValue = @(85); #endif [tLayer addAnimation:musicAnimation forKey:@"musicAnimation"]; // 复制layer,会连layer的动画一同复制 _musicLayer.instanceCount = 3; // 复制的个数 _musicLayer.instanceTransform = CATransform3DMakeTranslation(30, 0, 0); //每个layer的间距。 _musicLayer.instanceDelay = 0.2; // 动画的延迟时间
重复图层之心形动画
心形的动画,一方面增加了路径的绘制,另一方面也包含了颜色的减弱效果。
// love路径 UIBezierPath *tPath = [UIBezierPath bezierPath]; [tPath moveToPoint:CGPointMake(SYS_DEVICE_WIDTH/2.0, 200)]; [tPath addQuadCurveToPoint:CGPointMake(SYS_DEVICE_WIDTH/2.0, 400) controlPoint:CGPointMake(SYS_DEVICE_WIDTH/2.0 + 200, 20)]; [tPath addQuadCurveToPoint:CGPointMake(SYS_DEVICE_WIDTH/2.0, 200) controlPoint:CGPointMake(SYS_DEVICE_WIDTH/2.0 - 200, 20)]; [tPath closePath]; // 具体的layer UIView *tView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; tView.center = CGPointMake(SYS_DEVICE_WIDTH/2.0, 200); tView.layer.cornerRadius = 5; tView.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0]; // 动作效果 CAKeyframeAnimation *loveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; loveAnimation.path = tPath.CGPath; loveAnimation.duration = 8; loveAnimation.repeatCount = MAXFLOAT; [tView.layer addAnimation:loveAnimation forKey:@"loveAnimation"]; CAReplicatorLayer *_loveLayer = [CAReplicatorLayer layer]; _loveLayer.instanceCount = 40; // 40个layer _loveLayer.instanceDelay = 0.2; // 每隔0.2出现一个layer _loveLayer.instanceColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0].CGColor; _loveLayer.instanceGreenOffset = -0.03; // 颜色值递减。 _loveLayer.instanceRedOffset = -0.02; // 颜色值递减。 _loveLayer.instanceBlueOffset = -0.01; // 颜色值递减。 [_loveLayer addSublayer:tView.layer]; [self.view.layer addSublayer:_loveLayer];
在 Demo 中,新增了类实现了一下CAReplicatorLayer的效果,这里就放个图欣赏下,具体代码查看 Demo 吧。
反射之倒影效果
使用CAReplicatorLayer并应用一个负比例变换于一个复制图层,你就可以创建指定视图(或整个视图层次)内容的镜像图片,这样就创建了一个实时的『反射』效果。
UIImage *image = [UIImage imageNamed:@"fireballoon"]; // 定义ReplicatorLayer CAReplicatorLayer *layer = [CAReplicatorLayer layer]; [layer setBounds:CGRectMake(0, 0, image.size.width, image.size.height * 1.5)]; layer.masksToBounds = YES; layer.anchorPoint = CGPointMake(0.5, 0.0); // 设置锚点为layer的中间上面部分 layer.position = CGPointMake(self.view.frame.size.width/2, 80.0); // 设置坐标 layer.instanceCount = 2; [self.view.layer addSublayer:layer]; // 设置图片的翻转并移动到合适的位置 CATransform3D transform = CATransform3DIdentity; transform = CATransform3DScale(transform, 1.0, -1.0, 1.0); transform = CATransform3DTranslate(transform, 0, -[image size].height * 2, 1.0); layer.instanceTransform = transform; // 设置子layer _imageLayer = [CALayer layer]; [_imageLayer setContentsScale:[[UIScreen mainScreen] scale]]; [_imageLayer setContents:(__bridge id)image.CGImage]; [_imageLayer setBounds:CGRectMake(0.0, 0.0, [image size].width, [image size].height)]; [_imageLayer setAnchorPoint:CGPointMake(0, 0)]; [layer addSublayer:_imageLayer];
当然,以view的形式来规定layer,也是一个很简单的方式。详见 Demo 。
其他效果
1、在后面追增一个形式,根据笔迹来运动,直接看动画吧,就不上代码了:
2、奉上苹果官方效果:看到效果的我乐起来~~
CAScrollLayer
对于一个未转换的图层,它的bounds和它的frame是一样的,frame属性是由bounds属性自动计算而出的,所以更改任意一个值都会更新其他值。
但是如果你只想显示一个大图层里面的一小部分呢。比如说,你可能有一个很大的图片,你希望用户能够随意滑动,或者是一个数据或文本的长列表。在一个典型的iOS应用中,你可能会用到UITableView或是UIScrollView,但是对于独立的图层来说,什么会等价于刚刚提到的UITableView和UIScrollView呢?
之前,探索了图层的contentsRect属性的用法,它的确是能够解决在图层中小地方显示大图片的解决方法。但是如果你的图层包含子图层那它就不是一个非常好的解决方案,因为,这样做的话每次你想『滑动』可视区域的时候,你就需要手工重新计算并更新所有的子图层位置。
这个时候就需要CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:方法,它自动适应bounds的原点以便图层内容出现在滑动的地方。注意,这就是它做的所有事情。前面提到过,Core Animation并不处理用户输入,所以CAScrollLayer并不负责将触摸事件转换为滑动事件,既不渲染滚动条,也不实现任何iOS指定行为例如滑动反弹(当视图滑动超多了它的边界的将会反弹回正确的地方)。
不同于UIScrollView,我们定制的滑动视图类并没有实现任何形式的边界检查(bounds checking)。图层内容极有可能滑出视图的边界并无限滑下去。CAScrollLayer并没有等同于UIScrollView中contentSize的属性,所以当CAScrollLayer滑动的时候完全没有一个全局的可滑动区域的概念,也无法自适应它的边界原点至你指定的值。它之所以不能自适应边界大小是因为它不需要,内容完全可以超过边界。 那你一定会奇怪用CAScrollLayer的意义到底何在,因为你可以简单地用一个普通的CALayer然后手动适应边界原点啊。真相其实并不复杂,UIScrollView并没有用CAScrollLayer,事实上,就是简单的通过直接操作图层边界来实现滑动。
CAScrollLayer有一个潜在的有用特性。如果你查看CAScrollLayer的头文件,你就会注意到有一个扩展分类实现了一些方法和属性:
- (void)scrollPoint:(CGPoint)p; - (void)scrollRectToVisible:(CGRect)r; @property(readonly) CGRect visibleRect;
看到这些方法和属性名,你也许会以为这些方法给每个CALayer实例增加了滑动功能。但是事实上他们只是放置在CAScrollLayer中的图层的实用方法。scrollPoint:方法从图层树中查找并找到第一个可用的CAScrollLayer,然后滑动它使得指定点成为可视的。scrollRectToVisible:方法实现了同样的事情只不过是作用在一个矩形上的。visibleRect属性决定图层(如果存在的话)的哪部分是当前的可视区域。如果你自己实现这些方法就会相对容易明白一点,但是CAScrollLayer帮你省了这些麻烦,所以当涉及到实现图层滑动的时候就可以用上了。
具体代码查看 Demo 。
CATransformLayer
当我们在构造复杂的3D事物的时候,如果能够组织独立元素就太方便了。比如说,你想创造一个孩子的手臂:你就需要确定哪一部分是孩子的手腕,哪一部分是孩子的前臂,哪一部分是孩子的肘,哪一部分是孩子的上臂,哪一部分是孩子的肩膀等等。
当然是允许独立地移动每个区域的啦。以肘为指点会移动前臂和手,而不是肩膀。Core Animation图层很容易就可以让你在2D环境下做出这样的层级体系下的变换,但是3D情况下就不太可能,因为所有的图层都把他的孩子都平面化到一个场景中。
CATransformLayer解决了这个问题,CATransformLayer不同于普通的CALayer,因为它不能显示它自己的内容。只有当存在了一个能作用域子图层的变换它才真正存在。CATransformLayer并不平面化它的子图层,所以它能够用于构造一个层级的3D结构,比如我的手臂示例。
CATiledLayer
CATiledLayer,是载入大图时对性能提升有帮助的layer,将大图分解成小片然后将他们单独按需载入。不详解。
GitHub地址
1、Demo地址:
2、为了更好的理解各种layer的,推荐一下开源软件,里面集合和多种layer的效果: