Files
knowledge-kit/Chapter1 - iOS/1.25.md
2020-02-25 17:46:51 +08:00

211 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 复制层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 所指的位置。