Core Animation总结(一)图层变换(平面 立体)
#Core Animation
###CAShapeLayer CAShapeLayer是一个通过矢量图形而不是bitmap来绘制的图层子类。你指定诸如颜色和线宽等属性,用CGPath来定义想要绘制的图形,最后CAShapeLayer就自动渲染出来了
- 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
- 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
- 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉
- 不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
CAShapeLayer可以用来绘制所有能够通过CGPath来表示的形状。这个形状不一定要闭合,图层路径也不一定要不可破,事实上你可以在一个图层上绘制好几个不同的形状。你可以控制一些属性比如lineWith(线宽,用点表示单位),lineCap(线条结尾的样子),和lineJoin(线条之间的结合点的样子);但是在图层层面你只有一次机会设置这些属性。如果你想用不同颜色或风格来绘制多个形状,就不得不为每个形状准备一个图层了。
// 用CAShapeLayer绘制一个火柴人
// 用UIBezierPath创建图层路径
let path: UIBezierPath = UIBezierPath()
// 画圆
path.move(to: CGPoint(x: 175, y: 100))
path.addArc(withCenter: CGPoint(x: 150, y: 100), radius: 25, startAngle: 0, endAngle: CGFloat.pi*2, clockwise: true)
// 竖线 身子
path.move(to: CGPoint(x: 150, y: 125))
path.addLine(to: CGPoint(x: 150, y: 175))
// 斜线 腿
path.addLine(to: CGPoint(x: 125, y: 225))
// 斜线 腿
path.move(to: CGPoint(x: 150, y: 175))
path.addLine(to: CGPoint(x: 175, y: 225))
path.move(to: CGPoint(x: 100, y: 150))
path.addLine(to: CGPoint(x: 200, y: 150))
let shapeLayer:CAShapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 5
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineCap = kCALineCapRound
shapeLayer.path = path.cgPath
self.view.layer.addSublayer(shapeLayer)
// 有2个圆角一个直角的矩形
let rect: CGRect = CGRect(x: 100, y: 100, width: 150, height: 100)
let size: CGSize = CGSize(width: 20, height: 20)
// 右上 右下 圆角
let corners: UIRectCorner = [UIRectCorner.topRight,UIRectCorner.bottomRight]
let path: UIBezierPath = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: size)
let shapeLayer:CAShapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.red.cgColor
shapeLayer.fillColor = UIColor.clear.cgColor
shapeLayer.lineWidth = 5
shapeLayer.lineJoin = kCALineJoinRound
shapeLayer.lineCap = kCALineCapRound
shapeLayer.path = path.cgPath
self.view.layer.addSublayer(shapeLayer)
###CATextLayer Core Animation提供了一个CALayer的子类CATextLayer,它以图层的形式包含了UILabel几乎所有的绘制特性,并且额外提供了一些新的特性。
CATextLayer使用了Core text,并且渲染得非常快。
let v = UIView(frame: CGRect(x: 100, y: 100, width: 150, height: 100))
self.view.addSubview(v)
let textLayer: CATextLayer = CATextLayer()
textLayer.frame = v.bounds
v.layer.addSublayer(textLayer)
textLayer.foregroundColor = UIColor.black.cgColor
textLayer.alignmentMode = kCAAlignmentJustified
textLayer.isWrapped = true
/*
contentScale属性,用来决定图层内容应该以怎样的分辨率来渲染。
contentsScale并不关心屏幕的拉伸因素而总是默认为1.0。
如果我们想以Retina的质量来显示文字,我们就得手动地设置CATextLayer的contentsScale属性
*/
textLayer.contentsScale = UIScreen.main.scale
// 纯文本
let txt = "用NSAttributedString实现一个富文本标签。"
// 富文本
let att: NSMutableAttributedString = NSMutableAttributedString(string: txt)
textLayer.string = txt
textLayer.string = att
###CATransformLayer
//
// ViewController.swift
// Demo
//
// Created by joker on 2016/11/9.
// Copyright © 2016年 joker. All rights reserved.
//
import UIKit
import GLKit
class ViewController: UIViewController {
var v: UIView!
override func viewDidLoad() {
super.viewDidLoad()
v = UIView(frame: CGRect(x: 50, y: 50, width: 200, height: 300))
v.backgroundColor = UIColor(white: 0.7, alpha: 0.3)
self.view.addSubview(v)
var pt: CATransform3D = CATransform3DIdentity
pt.m34 = -1/500
self.view.layer.sublayerTransform = pt
var c1t: CATransform3D = CATransform3DIdentity
c1t = CATransform3DTranslate(c1t, -100, 0, 0)
let cube1:CALayer = cube(transform: c1t)
v.layer.addSublayer(cube1)
var c2t: CATransform3D = CATransform3DIdentity
c2t = CATransform3DTranslate(c2t, 100, 0, 0)
c2t = CATransform3DRotate(c2t, -CGFloat.pi/4, 1, 0, 0)
c2t = CATransform3DRotate(c2t, -CGFloat.pi/4, 0, 1, 0)
let cube2: CALayer = cube(transform: c2t)
v.layer.addSublayer(cube2)
}
func cube(transform:CATransform3D) -> CALayer {
let cube: CATransformLayer = CATransformLayer()
//1
var ct:CATransform3D = CATransform3DMakeTranslation(0, 0, 50)
cube.addSublayer(self.face(transform: ct))
//2
ct = CATransform3DMakeTranslation(50, 0, 0)
ct = CATransform3DRotate(ct, CGFloat.pi/2, 0, 1, 0)
cube.addSublayer(self.face(transform: ct))
//3
ct = CATransform3DMakeTranslation(0, -50, 0)
ct = CATransform3DRotate(ct, CGFloat.pi/2, 1, 0, 0)
cube.addSublayer(self.face(transform: ct))
//4
ct = CATransform3DMakeTranslation(0, 50, 0)
ct = CATransform3DRotate(ct, -CGFloat.pi/2, 1, 0, 0)
cube.addSublayer(self.face(transform: ct))
//5
ct = CATransform3DMakeTranslation(-50, 0, 0)
ct = CATransform3DRotate(ct, -CGFloat.pi/2, 0, 1, 0)
cube.addSublayer(self.face(transform: ct))
//6
ct = CATransform3DMakeTranslation(0, 0, -50)
ct = CATransform3DRotate(ct, CGFloat.pi, 0, 1, 0)
cube.addSublayer(self.face(transform: ct))
let vSize: CGSize = v.bounds.size
cube.position = CGPoint(x: vSize.width/2, y: vSize.height/2)
cube.transform = transform
return cube
}
func face(transform:CATransform3D) -> CALayer{
let face:CALayer = CALayer()
face.frame = CGRect(x: -50, y: -50, width: 100, height: 100)
face.transform = transform
// 随机颜色
let red: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0)
let green: CGFloat = CGFloat( arc4random_uniform(255))/CGFloat(255.0)
let blue: CGFloat = CGFloat(arc4random_uniform(255))/CGFloat(255.0)
let color: UIColor = UIColor(red: red, green: green, blue: blue, alpha: 0.3)
// 背景色
face.backgroundColor = color.cgColor
return face
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
###CAGradientLayer CAGradientLayer是用来生成两种或更多颜色平滑渐变的,使用了硬件加速。
//CAGradientLayer是用来生成两种或更多颜色平滑渐变的
let gradientLayer:CAGradientLayer = CAGradientLayer()
gradientLayer.frame = CGRect(x: 100, y: 100, width: 150, height: 100)
self.view.layer.addSublayer(gradientLayer)
/*
colors属性可以包含很多颜色,所以创建一个彩虹一样的多重渐变也是很简单的。
默认情况下,这些颜色在空间上均匀地被渲染
红色 绿色 蓝色 渐变
*/
gradientLayer.colors = [UIColor.red.cgColor, UIColor.green.cgColor, UIColor.blue.cgColor]
/*
locations属性是一个浮点数值的数组
这些浮点数定义了colors属性中每个不同颜色的位置,
0.0代表着渐变的开始,1.0代表着结束。
*/
gradientLayer.locations = [0.0, 0.3, 0.5]
/*
startPoint和endPoint属性,他们决定了渐变的方向
左上角坐标是{0, 0},右下角坐标是{1, 1}
*/
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
###CAReplicatorLayer CAReplicatorLayer的目的是为了高效生成许多相似的图层 变换是逐步增加的,每个实例都是相对于前一实例布局 用CAReplicatorLayer生成十个图层组成一个圆圈。
let replicatorLayer: CAReplicatorLayer = CAReplicatorLayer()
replicatorLayer.frame = CGRect(x: 50, y: 50, width: 200, height: 150)
self.view.layer.addSublayer(replicatorLayer)
var transform: CATransform3D = CATransform3DIdentity
transform = CATransform3DTranslate(transform, 0, 100, 0)
transform = CATransform3DRotate(transform, CGFloat.pi/5, 0, 0, 1)
transform = CATransform3DTranslate(transform, 0, -100, 0)
// instanceTransform指定了一个CATransform3D3D变换
replicatorLayer.instanceTransform = transform
// instanceCount属性指定了图层需要重复多少次。
replicatorLayer.instanceCount = 10
/*
用instanceBlueOffset和instanceGreenOffset属性 通过逐步减少蓝色和绿色通道
图层在重复的时候,颜色变化
*/
replicatorLayer.instanceBlueOffset = -0.1
replicatorLayer.instanceGreenOffset = -0.1
// 透明递减
replicatorLayer.instanceAlphaOffset = -0.1
let layer: CALayer = CALayer()
layer.frame = CGRect(x: 50, y: 50, width: 50, height: 50)
layer.backgroundColor = UIColor.gray.cgColor
replicatorLayer.addSublayer(layer)
###CAScrollLayer CAScrollLayer是CALayer的子类,用于显示层的一部分。CAScrollLayer的可滚动区域的范围是由它的子层布局来确定的。 CAScrollLayer不提供键盘或鼠标事件处理,也没有提供可见滚动条。
属性 scrollMode 允许滚动方向,可选以下值:
NSString * const kCAScrollNone; // 禁止滚动
NSString * const kCAScrollVertically; // 只允许垂直滚动
NSString * const kCAScrollHorizontally; // 只允许水平滚动
NSString * const kCAScrollBoth; // 可以随便滚动,默认
visibleRect 返回可见区域范围。 此属性是在CALayer的分类中实现的,所以所有CALayer子类都可以调用次方法来获取当前显示的可见区域范围。但是必须要是在CAScrollLayer的子图层。
方法 (void)scrollToPoint:(CGPoint)p 把指定点p滚动到左上角。点坐标可以是负值。
(void)scrollToRect:(CGRect)r 滚动使指定区域r可见。 如果r.size > self.bounds.size,则r.size = self.bounds.size。
(void)scrollPoint:(CGPoint)p 此方法是在CALayer的分类中实现。改方法是从自身开始往父图层找到最近的CAScrollLayer层,然后调用-scrollToPoint: 方法,如果没有找到CAScrollLayer层则不做任何处理。
(void)scrollRectToVisible:(CGRect)r 此方法是在CALayer的分类中实现。改方法是从自身开始往父图层找到最近的CAScrollLayer层,然后调用-scrollToRect: 方法,如果没有找到CAScrollLayer层则不做任何处理。
###CATiledLayer CATiledLayer为载入大图造成的性能问题提供了一个解决方案:将大图分解成小片然后将他们单独按需载入
256*256是CATiledLayer的默认小图大小,默认大小可以通过tileSize属性更改
CATiledLayer需要载入新的小图时,就会调用 drawLayer:inContext: 这个方法 比如用于地图显示
###CAEmitterLayer CAEmitterLayer是一个高性能的粒子引擎,被用来创建实时例子动画如:烟雾,火,雨等等这些效果。
CAEmitterLayer看上去像是许多CAEmitterCell的容器,这些CAEmitierCell定义了一个例子效果。你将会为不同的例子效果定义一个或多个CAEmitterCell作为模版,同时CAEmitterLayer负责基于这些模版实例化一个粒子流。一个CAEmitterCell类似于一个CALayer:它有一个contents属性可以定义为一个CGImage,另外还有一些可设置属性控制着表现和行为。
CAEmitterLayer的属性它自己控制着整个例子系统的位置和形状。一些属性比如birthRate,lifetime和celocity,这些属性在CAEmitterCell中也有。这些属性会以相乘的方式作用在一起
//在一圆中发射不同速度和透明度的粒子创建一个火爆炸的效果
let v = UIView(frame: CGRect(x: 50, y: 50, width: 200, height: 200))
self.view.addSubview(v)
// CAEmitterLayer是一个高性能的粒子引擎,被用来创建实时例子动画如:烟雾,火,雨等等这些效果
let emitterLayer: CAEmitterLayer = CAEmitterLayer()
emitterLayer.frame = v.bounds
v.layer.addSublayer(emitterLayer)
/*
renderMode,控制着在视觉上粒子图片是如何混合的。
kCAEmitterLayerAdditive 合并例子重叠部分的亮度使得看上去更亮
*/
emitterLayer.renderMode = kCAEmitterLayerAdditive
emitterLayer.emitterPosition = CGPoint(x: emitterLayer.frame.size.width/2, y: emitterLayer.frame.size.height/2)
//是否将3D例子系统平面化到一个图层(默认值)或者可以在3D空间中混合其他的图层
emitterLayer.preservesDepth = true
//CAEmitterLayer看上去像是许多CAEmitterCell的容器,这些CAEmitierCell定义了一个例子效果
let cell: CAEmitterCell = CAEmitterCell()
cell.contents = UIImage(named: "bg.jpg")?.cgImage
cell.birthRate = 150
cell.lifetime = 5.0
//一个可以混合图片内容颜色的混合色
cell.color = UIColor.red.cgColor
/*
将alphaSpeed设置为-0.4,
透明度每过一秒就是减少0.4,
这样就有发射出去之后逐渐小时的效果。
*/
cell.alphaSpeed = -0.4
cell.velocity = 50
cell.velocityRange = 50
/*
emissionRange属性的值是2π,从360度任意位置反射出来。
如果指定一个小一些的值,就可以创造出一个圆锥形
*/
cell.emissionRange = CGFloat.pi*2
emitterLayer.emitterCells = [cell]
###CAEAGLLayer OpenGL提供了Core Animation的基础,它是底层的C接口,直接和iPhone,iPad的硬件通信,极少地抽象出来的方法。OpenGL没有对象或是图层的继承概念。它只是简单地处理三角形。OpenGL中所有东西都是3D空间中有颜色和纹理的三角形。
CAEAGLLayer用来显示任意的OpenGL图形。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *glView;
@property (nonatomic, strong) EAGLContext *glContext;
@property (nonatomic, strong) CAEAGLLayer *glLayer;
@property (nonatomic, assign) GLuint framebuffer;
@property (nonatomic, assign) GLuint colorRenderbuffer;
@property (nonatomic, assign) GLint framebufferWidth;
@property (nonatomic, assign) GLint framebufferHeight;
@property (nonatomic, strong) GLKBaseEffect *effect;

@end
@implementation ViewController
- (void)setUpBuffers
{
//set up frame buffer
glGenFramebuffers(1, &_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
//set up color render buffer
glGenRenderbuffers(1, &_colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);
[self.glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.glLayer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_framebufferWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_framebufferHeight);
//check success
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"Failed to make complete framebuffer object: %i", glCheckFramebufferStatus(GL_FRAMEBUFFER));
}
}
- (void)tearDownBuffers
{
if (_framebuffer) {
//delete framebuffer
glDeleteFramebuffers(1, &_framebuffer);
_framebuffer = 0;
}
if (_colorRenderbuffer) {
//delete color render buffer
glDeleteRenderbuffers(1, &_colorRenderbuffer);
_colorRenderbuffer = 0;
}
}
- (void)drawFrame {
//bind framebuffer & set viewport
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
glViewport(0, 0, _framebufferWidth, _framebufferHeight);
//bind shader program
[self.effect prepareToDraw];
//clear the screen
glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 1.0);
//set up vertices
GLfloat vertices[] = {
-0.5f, -0.5f, -1.0f, 0.0f, 0.5f, -1.0f, 0.5f, -0.5f, -1.0f,
};
//set up colors
GLfloat colors[] = {
0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
};
//draw triangle
glEnableVertexAttribArray(GLKVertexAttribPosition);
glEnableVertexAttribArray(GLKVertexAttribColor);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glVertexAttribPointer(GLKVertexAttribColor,4, GL_FLOAT, GL_FALSE, 0, colors);
glDrawArrays(GL_TRIANGLES, 0, 3);
//present render buffer
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
[self.glContext presentRenderbuffer:GL_RENDERBUFFER];
}
- (void)viewDidLoad
{
[super viewDidLoad];
//set up context
self.glContext = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:self.glContext];
//set up layer
self.glLayer = [CAEAGLLayer layer];
self.glLayer.frame = self.glView.bounds;
[self.glView.layer addSublayer:self.glLayer];
self.glLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking:@NO, kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8};
//set up base effect
self.effect = [[GLKBaseEffect alloc] init];
//set up buffers
[self setUpBuffers];
//draw frame
[self drawFrame];
}
- (void)viewDidUnload
{
[self tearDownBuffers];
[super viewDidUnload];
}
- (void)dealloc
{
[self tearDownBuffers];
[EAGLContext setCurrentContext:nil];
}
@end
###AVPlayerLayer AVPlayerLayer不是Core Animation框架的一部分(AV前缀看上去像),AVPlayerLayer是有别的框架(AVFoundation)提供的,它和Core Animation紧密地结合在一起,提供了一个CALayer子类来显示自定义的内容类型。