前言:
在app开发中,我们经常会使用一些动画来增加应用的炫酷效果,从而达到吸引用户的目的。QuartzCore框架是做动画的基础,其实他就是CoreAnimation。这个框架的头文件只包含了CoreAnimation.h。
UIView和CALayer
相同点:
1、同样是一些被层级关系树管理的矩形块;
2、同样也可以包含图片、文字等内容,
3、同样可用来做动画和变换,可以管理子图层等功能,
4、平行层关系,做职责分离,避免许多重复的代码
不同点:
1、视图的职责就是创建并管理这个图层,以确保当子视图在层级关系中添加或者移除的时候,他们关联的图层也同样对应在层级关系树当中有相同的操作。
2、CALayer不回去处理用户的交互事件。
一、简单图层的使用
// 创建简单的图层
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(100, 100, 200, 200);
layer.backgroundColor = [UIColor redColor].CGColor;
layer.contents = (id)[UIImage imageNamed:@"阿狸头像"].CGImage;
[self.view.layer addSublayer:layer];
二、CALayer的属性
1、contents属性
// 创建图层
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(100, 100, 200, 200);
layer.position = self.view.center;
layer.backgroundColor = [UIColor redColor].CGColor;
// 设置图层内容
layer.contents = (__bridge id)([UIImage imageNamed:@"狸头像"].CGImage);
[self.view.layer addSublayer:layer];
2、contentGravity属性
// 创建图层
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(100, 100, 200, 200);
layer.position = self.view.center;
layer.backgroundColor = [UIColor redColor].CGColor;
// 设置图层内容
layer.contents = (__bridge id)([UIImage imageNamed:@"狸头像"].CGImage);
// 设置填充模式
layer.contentsGravity = kCAGravityResizeAspect;
[self.view.layer addSublayer:layer];
3、contentsScale属性
// 创建图层
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(100, 100, 200, 200);
layer.position = self.view.center;
layer.backgroundColor = [UIColor redColor].CGColor;
// 设置图层内容
layer.contents = (__bridge id)([UIImage imageNamed:@"狸头像"].CGImage);
// 设置填充模式
layer.contentsGravity = kCAGravityCenter;
// 设置 contentsScale
layer.contentsScale = 2; // 一般设置为 [UIScreen mainScreen].scale,可根据设备自动调整
// 将其放大,让其模糊来演示contentsScale
layer.transform = CATransform3DMakeScale(3, 3, 0);
//添加到视图关联的图层上
[self.view.layer addSublayer:layer];
4、contentsRect属性:裁剪的效果,范围是 {0, 0, 1, 1}
// 创建图层
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(0, 0, 200, 200);
layer.position = self.view.center;
layer.backgroundColor = [UIColor redColor].CGColor;
// 设置图层内容
layer.contents = (__bridge id)([UIImage imageNamed:@"阿狸头像"].CGImage);
// 设置 contentsRect
layer.contentsRect = CGRectMake(0, 0, 0.5, 0.5);
//添加到视图关联的图层上
[self.view.layer addSublayer:layer];
5、contentsCenter:将图片进行局部拉伸,contentsCenter 是一个 CGRect,它定义了一个固定的边框和一个在图层上可拉伸的区域。默认情况下,contentsCenter 的拉伸区域是{0, 0, 1, 1}``
6、绘制图形
在 UIView 中,我们可以重写 drawRect: 来绘制图形(开发者可以调用setNeedsDisplay方法触发)。实质上该方法封装了 CALayer 的绘制方法。
-(void)drawRectWithLayer{
// 创建子图层
CALayer* subLayer = [CALayer new];
// 布局
subLayer.frame = CGRectMake(0, 0, 150, 150);
subLayer.position = self.view.center;
// 设置颜色
subLayer.backgroundColor = UIColor.groupTableViewBackgroundColor.CGColor;
// 设置代理
subLayer.delegate = self;
// 添加到视图关联图层上
[self.view.layer addSublayer:subLayer];
// 触发 CALayer 的绘制
[subLayer display];
/*
-display 触发代理方法
1、- (void)displayLayer:(CALayerCALayer *)layer;
2、- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx;
*/
}
// 完成绘制的代理方法
-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
CGContextSetLineWidth(ctx, 10.0f);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextStrokeEllipseInRect(ctx, layer.bounds);
}
三、CALayer的几何相关
1、布局
frame,bounds 和 position(中心的意思)
2、锚点
anchorPoint :对于整个图形来表示是(1,1),某个点在这个图形的位置比例。
3、坐标系
//不同坐标系之间的图层转换提供了一些工具类方法
- (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;
//调整视图的层级关系
- (void)insertSublayer:(CALayer *)layer atIndex:(unsigned)idx;
- (void)insertSublayer:(CALayer *)layer below:(nullable CALayer *)sibling;
- (void)insertSublayer:(CALayer *)layer above:(nullable CALayer *)sibling;
4、Hit Testing -- 两种方法
CGPoint point = [[touches anyObject] locationInView:self.view];
// 将落点转换到红色背景的图层的层级上,以判断是否被包含
point = [self.subLayer convertPoint:point fromLayer:self.view.layer];
#pragma mark - 判断一个点是否在图层的 frame 范围内,如果在就返回 YES,反之为 NO
if ([self.subLayer containsPoint:point]) {
[[[UIAlertView alloc] initWithTitle:@"在红色图层中"
message:nil
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
else{
[[[UIAlertView alloc] initWithTitle:@"未在红色图层中"
message:nil
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}
#pragma mark - 接受一个点,但是它返回的是包含这个点的图层
// CALayer *layer = [self.view.layer hitTest:point];
// if (layer == self.subLayer) {
// [[[UIAlertView alloc] initWithTitle:@"在红色图层中"
// message:nil
// delegate:nil
// cancelButtonTitle:@"OK"
// otherButtonTitles:nil] show];
// }else{
//
// [[[UIAlertView alloc] initWithTitle:@"未在红色图层中"
// message:nil
// delegate:nil
// cancelButtonTitle:@"OK"
// otherButtonTitles:nil] show];
// }
四、CALayer的特殊效果
1、maskToBounds
maskToBounds 可以切除超出图层的部分,UIView中类似的有 clipsToBounds。
2、 borderWidth(边宽) 和 borderColor(边颜色)
3、阴影
/*
shadowOpacity 控制图层阴影的透明度
shadowColor 阴影的颜色
shadowOffset 阴影的方向和距离
shadowRadius 阴影的的模糊度
注意:
阴影效果不能使用masksToBounds,如若使用阴影效果会消失
*/
CALayer *layer = [CALayer new];
layer.frame = CGRectMake(50, 100, 150, 150);
layer.position = self.view.layer.position;
layer.backgroundColor = UIColor.redColor.CGColor;
// 先 添加到视图关联图层上
[self.view.layer addSublayer:layer];
// 阴影层做阴影部分的设置
layer.cornerRadius = 10;
layer.shadowOpacity = 1;
layer.shadowColor = UIColor.brownColor.CGColor;
layer.shadowOffset = CGSizeMake(0, -3); //默认值
layer.shadowRadius = 10;
五、mask蒙版/遮罩
蒙版/遮罩解决了展现的内容是一个不规则的形状,CALayer 有一个属性 mask,这个属性本身就是一个 CALayer 类型,它的内容轮廓定义了父图层的可见部分,其他部分则会被抛弃。如下图:
@interface LayerMaskController ()
@property (strong, nonatomic)CALayer* maskLayer1;
@property (strong, nonatomic)CAShapeLayer* maskLayer2;
@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIImageView *imageView1;
@property (weak, nonatomic) IBOutlet UIImageView *imageView2;
@end
@implementation LayerMaskController
- (void)viewDidLoad {
[super viewDidLoad];
self.imageView1.layer.mask = self.maskLayer1;
self.imageView2.layer.mask = self.maskLayer2;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
animation.duration = 1.0f;
animation.toValue = @(M_PI);
animation.autoreverses = YES;
animation.repeatCount = HUGE_VALF; //无穷大,循环
[self.maskLayer1 addAnimation:animation forKey:@"animation"];
[self.maskLayer2 addAnimation:animation forKey:@"animation"];
}
// 寄宿图为图片
-(CALayer *)maskLayer1{
if (_maskLayer1==nil) {
_maskLayer1 = [CALayer new];
_maskLayer1.frame = self.imageView.bounds;
UIImage* image = [UIImage imageNamed:@"clip"];
_maskLayer1.contents = (__bridge id)image.CGImage;
}
return _maskLayer1;
}
// 寄宿图为自定义图形
-(CAShapeLayer *)maskLayer2{
if (_maskLayer2==nil) {
_maskLayer2 = [CAShapeLayer new];
_maskLayer2.frame = self.imageView.bounds;
UIBezierPath* path = [UIBezierPath bezierPathWithOvalInRect:_maskLayer1.bounds];
_maskLayer2.path = path.CGPath;
}
return _maskLayer2;
}
六、图层的变换
CALayer 做二维空间变换的属性叫做 affineTransform,而其属性 transform 是一个 CATransform3D 类型,可以将图层在三维空间变换效果。