《iOS动画》读书笔记·内容层动画

《iOS动画》读书笔记·前序
《iOS动画》读书笔记·显示层动画
《iOS动画》读书笔记·内容层动画
《iOS动画》读书笔记·转场动画

UIView和CALayer的区别:

(1)UIView继承UIResponder,因此UIView可以处理响应事件,而CALayer继承NSObject,所以它只负责内容的创建、绘制。
(2)UIView负责对内容的管理,而CALayer负责对内容的绘制
(3)UIView中的位置属性只有frame,bounds,center,而CALayer除了这些之外还有anchorPoint,position
(4)通过修改CALayer可以实现UIView无法实现的很多高级功能

Core Animation核心动画

Core Animation为iOS核心动画,它提供了一组丰富的API可以用于制作各种高级炫酷的动画效果。CoreAnimation来自iOS的QuartzCore.framework框架,它具备以下特点:
(1)直接作用于CALayer图层上
(2)Core Animation的执行过程在后台执行,不阻塞主线程
(3)可用CALayer绝大多数属性绘制高级动画效果

Core Animation下各种常用动画类的继承关系图

《iOS动画》读书笔记·内容层动画_第1张图片
Core Animation下各种常用动画类的继承关系图
  • @protocol CAMediaTiming有很多动画公共的属性,比如常见的 duration / speed / repeatCount
  • CAAnimation 主要用于实现动画的委托代理方法,比如动画的开始事件和结束事件
  • CAPropertyAnimation 为动画属性,分为CABasicAnimation基础动画和CAKeyframeAnimation关键帧动画
  • CAAnimationGroup 组合动画
    -CATransition 转场动画

CABasicAnimation基础动画合集

(1)位置动画
posion
注意animation.toValue属性表明改变了控件的位置,所以给一个新的position的x,y坐标 animation.byValue表明在控件原来位置的基础上,沿x,y坐标分别移动了多少

(2)缩放动画
transform.scale.x / transform.scale.y,scale有x,y两个属性表明当前UI控件的width/height的缩放系数

(3)旋转动画
transform.rotation

(4)位移动画
transform.translation.x / transform.translation.y 表示在x/y方向上移动

(5)边框动画
animation.keyPath = "borderWidth"

(6)颜色渐变
animation.keyPath = "backgroundColor"
animation.keyPath = "borderColor"

(7)淡入淡出
animation.keyPath = "opacity"

(8)阴影渐变
animation.keyPath = "shadowOffset"

这里就不举具体的例子了,可以参考这篇→_→核心动画-Core Animation

Core Animation:

关键字 CAKeyframeAnimation
CAKeyframeAnimation:是CALayer层下的关键帧动画类,实现类似UIView的关键帧动画效果(前面有介绍)。ACKeyframeAnimationCAPropertyAnimation的一个子类,与CABasicAnimation原理类似,都是通过修改当前CALayer图层的value属性实现动画效果。不同的是CABasicAnimation一般只使用fromeValue/toValue/byValue,即只能修改一个value值。而CAKeyframeAnimation则可以修改一组value值来实现对动画的更精确细腻的控制。

CAAnimation Group:组合动画,把各种动画组合起来形成各种效果

CAKeyframeAnimation动画属性要点:
(1)values
数组类型,数组中的每个元素描述了一个关键帧的相关属性。比如描述关键帧位置的动画时,values描述的是位置信息;比如描述关键帧缩放动画时,values描述的是缩放比例信息
(2)keyTimes
关键帧在动画周期默认是匀速的。如果设置了该属性,那么每个关键帧显示的周期为keyTimes*duration
(3)path
控制动画路径,通过设置CGPathRef/CGMutablePathRef可以让动画按照自己绘制的路径运行

案例

具体示例代码看这里→_→https://github.com/SPIREJ/AnimationLayerSwift

1、CAEmitterCell粒子火焰效果

关键字:CAEmitterLayer / ACEmitterCell

粒子系统由两部分组成:CAEmitterLayer CAEmitterCell
(1)CAEmitterLayer为粒子发射图层。该图层主要用于控制粒子展现范围、粒子发射位置、粒子发射形状、渲染模式等。通过CAEmitterCell构建的发射单元都受到CAEmitterLayer图层节制。
(2)CAEmitterCell粒子发射单元,控制粒子的移动速度、方向、范围等等,这些属性可以制作酷炫的粒子特效动画

粒子火焰效果.gif
class EmitterFireVC: UIViewController {
    
    let emitterCell = CAEmitterCell() //粒子单元cell
    let emitterLayer = CAEmitterLayer()//粒子发射图层

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.backgroundColor = UIColor.black;
        
        // Do any additional setup after loading the view.
        self.navigationItem.title = "粒子火焰效果"
        
        emitterCell.name = "fire"//粒子单元名称
        emitterCell.emissionLongitude = CGFloat(Double.pi)//该d粒子系统在xy平面的发射角度
        emitterCell.velocity = -1//粒子速度1,负表示沿y反方向运动
        emitterCell.velocityRange = 50//粒子速度范围
        emitterCell.emissionRange = 1.1//粒子发射角度
        emitterCell.yAcceleration = -200//粒子y方向的加速度分量
        emitterCell.scaleSpeed = 0.3//缩放比例,超大火苗
        emitterCell.birthRate = 500//粒子数
        emitterCell.lifetime = 1//粒子生命周期
        // 火焰颜色
        emitterCell.color = UIColor(red: 0.8, green: 0.4, blue: 0.2, alpha: 0.1).cgColor
        // 必须要设置粒子的“种子”内容
        emitterCell.contents = UIImage(named: "fire.png")!.cgImage
        
        emitterLayer.position = self.view.center//粒子发射位置
        emitterLayer.emitterSize = CGSize(width: 50, height: 10)//当前火苗大小
        // 当前粒子渲染模式 和 发射源模式
        emitterLayer.renderMode = .additive
        emitterLayer.emitterMode = .outline
        emitterLayer.emitterShape = .line
        // 粒子单元部署到粒子发射图层中
        emitterLayer.emitterCells = [emitterCell]
        self.view.layer.addSublayer(emitterLayer)
    }

    // 粒子速度
    @IBAction func velocityChanged(_ sender: UISlider) {
        // 重置粒子速度
        emitterLayer.setValue(sender.value, forKeyPath: "emitterCells.fire.velocity")
    }
    
    // 粒子数
    @IBAction func birthRateChanged(_ sender: UISlider) {
        // 重置粒子生成速度,即粒子数
        emitterLayer.setValue(sender.value, forKeyPath: "emitterCells.fire.birthRate")
    }
}

2、CAEmitterCell粒子霓虹效果

关键字:CAEmitterLayer / ACEmitterCell

《iOS动画》读书笔记·内容层动画_第2张图片
粒子霓虹效果.gif
class EmitterNenoVC: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        self.navigationItem.title = "霓虹效果"

        self.view.backgroundColor = UIColor.black
        
        let emitterCell = CAEmitterCell()
        emitterCell.name = "neonlight"
        emitterCell.emissionLongitude  = CGFloat(Double.pi*2)// emissionLongitude:x-y 平面的 发 射方向
        emitterCell.velocity = 50// 粒子速度
        emitterCell.velocityRange = 50// 粒子速度范围
        emitterCell.scaleSpeed = -0.2// 缩放比例 超大火苗
        emitterCell.scale = 0.1//缩放比例
        //R G B alpha的颜色速度渐变效果
        emitterCell.redSpeed = -0.2
        emitterCell.greenSpeed = -0.1
        emitterCell.blueSpeed = 0.1
        emitterCell.alphaSpeed = -0.2
        emitterCell.birthRate = 100//粒子生成速度
        emitterCell.lifetime = 4//粒子生命周期
        emitterCell.color = UIColor.white.cgColor//粒子背景颜色
        //粒子效果内容
        emitterCell.contents = UIImage(named: "neonlight.png")!.cgImage
        
        let emitterLayer = CAEmitterLayer()
        emitterLayer.position = self.view.center// 粒子发射位置
        emitterLayer.emitterSize = CGSize(width: 2, height: 2)// 控制粒子大小
        emitterLayer.renderMode = CAEmitterLayerRenderMode.backToFront//着色(渲染)模式
        emitterLayer.emitterMode = CAEmitterLayerEmitterMode.outline// 控制发射源模式 即形状
        emitterLayer.emitterShape = CAEmitterLayerEmitterShape.circle//形状
        
        emitterLayer.emitterCells = [emitterCell]
        self.view.layer.addSublayer(emitterLayer)
    }    
}

3、CAGradientLayer 颜色梯度/位置梯度

CAGradientLayer可以拆解为三个主要组成部分
CA:CoreAnimation的缩写,表明使用的是iOS框架下的核心动画部分
Gradient:梯度的意思,描述当前动画的特点,实现一些梯度功能的动画效果。比如位置的梯度变化、颜色的梯度渐变等
Layer:当前动画作用在Layer内容层

  • CALayer坐标系
    《iOS动画》读书笔记·内容层动画_第3张图片
    frame坐标系与“梯度”坐标系对应关系
属性
  • colors
    var colors: [AnyObject]?
    一个内部是CGColorRef的数组,规定所有的梯度所显示的颜色,默认为nil
  • locations
    var locations: [NSNumber]?
    一个内部是NSNumber的可选数组,规定所有的颜色梯度的区间范围,选值只能在0到1之间,并且数组的数据必须单增,默认值为nil
  • endPoint
    var endPoint: CGPoint
    图层颜色绘制的终点坐标,也就是阶梯图层绘制的结束点,默认值是(0.5,1.0)
  • startPoint
    var startPoint: CGPoint
    endPoint相互对应,就是绘制阶梯图层的起点坐标,绘制颜色的起点,默认值是(0.5,0.0)
  • type
    var type:String
    绘制类型,默认值是kCAGradientLayerAxial,也就是线性绘制,各个颜色阶层直接的变化是线性的
《iOS动画》读书笔记·内容层动画_第4张图片
GradientLayer颜色梯度变化.gif
override func viewDidLoad() {
    super.viewDidLoad()
    
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = UIColor.black
    
    let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 200))
    view.center = self.view.center
    self.view.addSubview(view)
    
    let gradientLayer:CAGradientLayer = CAGradientLayer()
    gradientLayer.frame = view.bounds
    gradientLayer.startPoint = CGPoint(x: 0, y: 0)
    gradientLayer.endPoint = CGPoint(x: 0, y: 1)
    gradientLayer.colors = [UIColor.clear.cgColor,UIColor.white.cgColor,UIColor.clear.cgColor]
    gradientLayer.locations = [0.0, 0.5, 1.0]
    view.layer.addSublayer(gradientLayer)
    
    let gradientAnimation:CABasicAnimation = CABasicAnimation()
    gradientAnimation.keyPath = "colors"
    gradientAnimation.fromValue = [UIColor.clear.cgColor,UIColor.white.cgColor,UIColor.clear.cgColor]
    gradientAnimation.toValue = [UIColor.blue.cgColor,UIColor.red.cgColor,UIColor.cyan.cgColor]
    
    //另一种方式 位置
    //gradientLayer.locations = [0.0, 0.05, 0.1]
    //gradientAnimation.keyPath = "locations"
    //gradientAnimation.fromValue = [0.0, 0.05, 0.1]
    //gradientAnimation.toValue = [0.9, 0.95, 1.0]
    
    gradientAnimation.duration = 3.0
    // 动画结束后保持结束后的状态
    gradientAnimation.isRemovedOnCompletion = false
    gradientAnimation.fillMode = .forwards
    
    gradientLayer.add(gradientAnimation, forKey: nil)

CAGradientLayer结合CoreAnimation可以实现很多酷炫的效果,有兴趣的朋友可以自行研究

4、CAReplicatorLayer图层复制

#关键字 CAReplicatorLayer
主要用于图层的快速复制,并且复制会保持被复制图层的各种基础属性及动画

基本属性

  • instanceCount
    var instanceCount: Int
    拷贝图层的次数,包括其所有的子图层,默认值是1,也就是没有任何子图层被复制

  • instanceDelay
    var instanceDelay: CFTimeInterval
    在短时间内的复制延时,一般用在动画上(支持动画的延时)

  • instanceTransform
    var instanceTransform: CATransform3D
    复制图层在被创建时产生的和上一个复制图层的位移(位移的锚点是CAReplicatorlayer的中心点)

  • preservesDepth
    var preservesDepth: Bool
    如果设置为YES,图层将保持于CATransformLayer类似的性质和相同的限制

  • instanceColor
    var instanceColor: CGColor?
    设置多个复制图层的颜色,默认位白色

  • instanceRedOffset
    var instanceRedOffset: Float
    设置每个复制图层相对上一个复制图层的红色偏移量

  • instanceGreenOffset
    var instanceGreenOffset: Float
    设置每个复制图层相对上一个复制图层的绿色偏移量

  • instanceBlueOffset
    var instanceBlueOffset: Float
    设置每个复制图层相对上一个复制图层的蓝色偏移量

  • instanceAlphaOffset
    var instanceAlphaOffset: Float
    设置每个复制图层相对上一个复制图层的透明度偏移量

(1) 图层复制 - 雷达效果
《iOS动画》读书笔记·内容层动画_第5张图片
图层复制 - 雷达效果.gif
override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.view.backgroundColor = UIColor.white
    
    // 背景视图
    let bgView = UIView(frame: CGRect(x: 0, y: 0, width: kWidth, height: 200))
    bgView.center = self.view.center
    bgView.backgroundColor = UIColor.lightGray
    self.view.addSubview(bgView)
    
    // 动画原图层,就是不断变大的那个圆
    let animationLayer = CAShapeLayer()
    animationLayer.backgroundColor = UIColor.red.cgColor
    animationLayer.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
    animationLayer.cornerRadius = 10
    animationLayer.position = CGPoint(x: kWidth/2.0, y: 100)
    
    // 动画 - 放大
    let transformScaleAnim = CABasicAnimation(keyPath: "transform")
    let value = NSValue.init(caTransform3D:CATransform3DMakeScale(10, 10, 1))
    transformScaleAnim.toValue = value
    
    // 动画 - 透明度
    let alphaAnim = CABasicAnimation(keyPath: "opacity")
    alphaAnim.toValue = 0
    
    // 加入到动画组
    let animGroup = CAAnimationGroup()
    animGroup.animations = [transformScaleAnim, alphaAnim]
    animGroup.duration = 2
    animGroup.repeatCount = HUGE
    animationLayer.add(animGroup, forKey: nil)
    
    // 复制图层
    let replicatorLayer = CAReplicatorLayer()
    replicatorLayer.addSublayer(animationLayer)
    replicatorLayer.instanceCount = 3 //复制图层数
    replicatorLayer.instanceDelay = 0.3//复制间隔时间
    
    // 加载到视图中显示
    bgView.layer.addSublayer(replicatorLayer)
}
(2) 图层复制 - 加载动画效果
《iOS动画》读书笔记·内容层动画_第6张图片
图层复制 - 加载动画效果.gif
func ui_circle() {
    
    // 背景视图
    let bgView = UIView(frame: CGRect(x: 0, y: 0, width: kWidth, height: 200))
    bgView.center = self.view.center
    bgView.backgroundColor = UIColor.lightGray
    self.view.addSubview(bgView)
    
    // 动画原图层,就是不断转圈的圆
    let animationLayer = CAShapeLayer()
    animationLayer.backgroundColor = UIColor.red.cgColor
    animationLayer.bounds = CGRect(x: 0, y: 0, width: 20, height: 20)
    animationLayer.cornerRadius = 10
    animationLayer.position = CGPoint(x: kWidth/2.0, y: 50)
    animationLayer.transform = CATransform3DMakeScale(0, 0, 0)//初始隐藏
    
    // 动画 - 缩放
    let transformAnim = CABasicAnimation(keyPath: "transform")
    transformAnim.duration = 2
    transformAnim.repeatCount = HUGE
    transformAnim.fromValue = NSValue.init(caTransform3D: CATransform3DMakeScale(1, 1, 1))
    transformAnim.toValue = NSValue.init(caTransform3D: CATransform3DMakeScale(0.1, 0.1, 0.1))
    animationLayer.add(transformAnim, forKey: nil)
    
    // 复制图层
    let replicatorLayer = CAReplicatorLayer()
    replicatorLayer.frame = bgView.bounds
    replicatorLayer.addSublayer(animationLayer)
    replicatorLayer.instanceCount = 10 //复制图层数
    replicatorLayer.instanceDelay = 0.2//复制间隔时间
    replicatorLayer.instanceTransform = CATransform3DMakeRotation(CGFloat(Double.pi*2/Double(10)), 0, 0, 1.0)
    
    // 加载到视图中显示
    bgView.layer.addSublayer(replicatorLayer)
}

完整示例代码https://github.com/SPIREJ/AnimationLayerSwift

如果您有兴趣的话
上一节《iOS动画》读书笔记·显示层动画
下一节《iOS动画》读书笔记·转场动画

你可能感兴趣的:(《iOS动画》读书笔记·内容层动画)