# 复制层(CAReplicatorLayer) > 对于下面的效果大家是否有实现思路? > > 有些人可能要说:老夫撸起袖子,敲键盘就是干,不需要手势交互,那么直接用5个**CALayer**,处理不同的位置以及定时器、透明度等等,貌似很简单。 > > 不不不,今天要带出来的主题是 **CAReplicatorLayer** ![音量柱动画效果图](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/QmW9ACfS9P5orau43H7gxuxsU4RVMDPD7mPnDKq4pgLmzr.gif) ## 1、CAReplicatorLayer > /* The replicator layer creates a specified number of copies of its > > - sublayers, each copy potentially having geometric, temporal and > - color transformations applied to it. > > * > > - Note: the CALayer -hitTest: method currently only tests the first > - instance of z replicator layer's sublayers. This may change in the > - future. */ 官方给出的意思就不翻译了,使用场景大致是一个形状、特性差不多的 layer,我们不需要重复创建,可以利用它来实现复制多个 layer ,然后通过 CAReplicatorLayer 的一些属性实现我们的需求。 上述效果的代码 ```objective-c //创建复制层,因为我们做的多个音量柱变化的动画都是一样的,所以创建了一个复制层,这个复制层可以对里面的 sublayer 进行复制,所以我们不需要重复创建了 CAReplicatorLayer *replicatorrLayer = [CAReplicatorLayer layer]; replicatorrLayer.frame = CGRectMake(0, 0, self.contentView.frame.size.width, self.contentView.frame.size.height); replicatorrLayer.backgroundColor = [UIColor blackColor].CGColor; self.replicatorrLayer = replicatorrLayer; [self.contentView.layer addSublayer:replicatorrLayer]; //创建音量震动条 CALayer *layer = [CALayer layer]; layer.backgroundColor = [UIColor whiteColor].CGColor; CGFloat width = 30; CGFloat height = 100; layer.bounds = CGRectMake(0, self.contentView.frame.size.height - height, width, height); layer.anchorPoint = CGPointMake(0, 1); layer.position = CGPointMake(0, self.contentView.frame.size.height); [self.contentView.layer addSublayer:layer]; //创建音量震动动画 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"]; animation.toValue = @0; animation.duration = 1; animation.repeatCount = MAXFLOAT; animation.autoreverses = YES; [layer addAnimation:animation forKey:nil]; [replicatorrLayer addSublayer:layer]; //* The number of copies to create, including the source object. replicatorrLayer.instanceCount = 6; //复制 sublayer 的个数,包括创建的第一个sublayer 在内的个数 replicatorrLayer.instanceDelay = 0.4; //设置动画延迟执行的时间 replicatorrLayer.instanceAlphaOffset = -0.15; //设置透明度递减 replicatorrLayer.instanceTransform = CATransform3DMakeTranslation(50, 0, 0); ``` [源码地址](https://github.com/FantasticLBP/BlogDemos/tree/master/复制层应用1-音量柱动画) ## 例子1 ![倒影效果](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/QmQrU8UxSytnKbWcDVpY5mdy6kmiSHpzyqwt8GykWKNEY2.png) 这里比较简单了,关键代码 ```objective-c CAReplicatorLayer *replicatorLayer = (CAReplicatorLayer *)self.view.layer; replicatorLayer.instanceCount = 2; replicatorLayer.instanceTransform = CATransform3DMakeRotation(M_PI, 1, 0, 0); replicatorLayer.instanceRedOffset -= 0.1; replicatorLayer.instanceGreenOffset -= 0.1; replicatorLayer.instanceBlueOffset -= 0.1; replicatorLayer.instanceAlphaOffset -= 0.3; ``` - 需要说明是这里我用 storyboard 处理的,因为已经拉好了控件,所以我们没办法将图片直接加到复制层上去。间接做法是将 UIViewController 的 view 的 layer 类型改变为 复制层 ``` //该方法返回 UIView 的层 //改写 UIView 的层:重写 layerClass 方法 + (Class)layerClass{ return [CAReplicatorLayer class]; } ``` [源码地址](https://github.com/FantasticLBP/BlogDemos/tree/master/复制层应用2-倒影效果) ## 例子2 ![复制层动画综合应用](https://raw.githubusercontent.com/FantasticLBP/knowledge-kit/master/assets/QQ20180610-235637-HD.gif) 需求分析: - 先画图。也就是添加一个滑动手势并监听它。然后强制绘图(self setNeedsDisplay) - 添加一个 layer 到 self.layer 上 - 改变当前 view 的 layer 类型。 ``` + (Class)layerClass{ return [CAReplicatorLayer class]; } ``` - 设置 CAReplicatorLayer 的 instanceCount 和 instanceDelay 属性 - 添加了小点,并为小点设置关键帧动画。 - 重置功能实现靠的是清除 path 上面的 points ,并移除 小点上面的动画 ``` #import "ViewControllerView.h" @interface ViewControllerView() @property (nonatomic, strong) UIBezierPath *path; @property (nonatomic, weak) CALayer *dotLayer; @end @implementation ViewControllerView + (Class)layerClass{ return [CAReplicatorLayer class]; } - (void)awakeFromNib{ [super awakeFromNib]; UIPanGestureRecognizer *tapGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(draw:)]; [self addGestureRecognizer:tapGesture]; self.path = [UIBezierPath bezierPath]; CALayer *layer = [CALayer layer]; layer.frame = CGRectMake(-UIScreen.mainScreen.bounds.size.width, 0, 15, 15); layer.backgroundColor = [UIColor orangeColor].CGColor; layer.cornerRadius = 7.5; self.dotLayer = layer; [self.layer addSublayer:layer]; CAReplicatorLayer *replicatorLayer = (CAReplicatorLayer *)self.layer; replicatorLayer.instanceCount = 20; replicatorLayer.instanceDelay = 0.25; } - (void)draw:(UIPanGestureRecognizer *)tap{ CGPoint currentPoint = [tap locationInView:self]; if (tap.state == UIGestureRecognizerStateBegan) { [self.path moveToPoint:currentPoint]; } else if(tap.state == UIGestureRecognizerStateChanged){ [self.path addLineToPoint:currentPoint]; [self setNeedsDisplay]; } } - (void)startAnimation{ //要实现动画围绕着给定的形状执行,那么需要关键帧动画(类比于Flash概念中的关键帧动画,只需要给定指定的关键帧,其余的帧系统会创建出来。)。关键帧动画的 path 和 values 是互斥的,也就是说如果设置了 values 还设置了 path 那么 path 属性会覆盖 values 属性。 CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"position"; animation.path = self.path.CGPath; animation.duration = 5; animation.repeatCount = MAXFLOAT; [self.dotLayer addAnimation:animation forKey:nil]; } - (void)redraw{ //清空路径:移除 path 上面所有的点,然后重绘 [self.path removeAllPoints]; [self setNeedsDisplay]; //移除动画 [self.dotLayer removeAllAnimations]; } - (void)drawRect:(CGRect)rect{ [self.path stroke]; } @end ``` [源码地址](https://github.com/FantasticLBP/BlogDemos/tree/master/复制层应用3-粒子闪烁效果) ### CALayer 层的动画有2个概念非常重要:AnchorPoint 和 position - postion 用来确定 layer 层在父层中的位置 - anchorPoint 用来确定 layer 身上哪个点会在 position 所指的位置。