UIView二维动画与CAAnimation核心动画

1 iOS动画

iOS的动画基本分为以下三种:

  • 仿射形变动画(基于UIView)
  • 核心动画(CAAnimation)
  • CG的动画

2仿射形变动画

  • frame动画
  • transform动画

设置UIView动画有以上两种常见的属性.
以上梁总动画都是基于UIView和CALayer的属性设置变化量. 不需要调用核心动画CAAnimation里面的专用类和API.
但是Frame必须要确定的指定形变前后的frame数值.比较受限制.相对来说. frame更适合进行平移的操作. 因为旋转需要通过各种计算来算出新的frame.搞得❤好累. 就有一些得不偿失. 这个时候就要知道. 如果是设置frame,bounds.center或者是使其形变的话. 可以用transform来替代frame. 会是更加明智的选择.

2.1UIView动画两种实现方式

  • beginAnimations 然后commitAnimations 把需要实现的代码放在中间的位置
UIView.beginAnimations("Move", context: nil)
UIView.setAnimationDuration(2)
UIView.setAnimationDelegate(self)
imageContentView.frame = CGRect(x: 80, y: 80, width: 200, height: 200)
UIView.commitAnimations()
  • 通过闭包或者block的方式
//初始状态transform
imageContentView.transform = CGAffineTransform.identity
UIView.animate(withDuration: 2.0) {
    imageContentView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
}

2.2形变属性设置的两种类型

  • UIView的 CGAffineTransform 类型属性:animatedView.transform
    一般是View的旋转,拉伸移动等属性,是二维的,通常使用都是前缀CGAffineTransform的类。

CGAffineTransform原理

CGAffineTransform形变是通过"仿射变换矩阵"来控制的,其中平移是矩阵相加,旋转与缩放则是矩阵相乘,为了合并矩阵运算中的加法和乘法,引入了齐次坐标的概念,它提供了用矩阵运算把二维、三维甚至高维空间中的一个点集从一个坐标系变换到另一个坐标系的有效方法.CGAffineTransform形变就是把二维形变使用一个三维矩阵来表示,其中第三列总是(0,0,1),形变通过前两列来控制,系统提供了CGAffineTransformMake结构体来控制形变
CGAffineTransformMake(CGFloat a, CGFloat b, CGFloat c, CGFloat d, CGFloat tx, CGFloat ty)
该三围变换矩阵如下

UIView二维动画与CAAnimation核心动画_第1张图片
三维变换矩阵

通过变换矩阵左乘向量,将空间中的一个点集从一个坐标系变换到另一个坐标系中,计算方式如下
UIView二维动画与CAAnimation核心动画_第2张图片
计算过程

计算结果

由此可知,其中tx用来控制在x轴方向上的平移,ty用来控制在y轴方向上的平移;a用来控制在x轴方向上的缩放,d用来控制在y轴方向上的缩放;abcd共同控制旋转

  • 平移CGAffineTransformMakeTranslation的原理
    imageContentView.transform = CGAffineTransform(translationX: 100, y: 100)
    imageContentView.transform = CGAffineTransform(a: 1, b: 0, c: 0, d: 1, tx: 100, ty: 100)
  • 缩放CGAffineTransformMakeScale原理
    imageContentView.transform = CGAffineTransform(scaleX: 2.0, y: 0.5)
    imageContentView.transform = CGAffineTransform(a: 2, b: 0, c: 0, d: 0.5, tx: 0, ty: 0)
  • 旋转CGAffineTransformMakeRotation原理
    imageContentView.transform = CGAffineTransform(rotationAngle: CGFloat(Double.pi * 0.5))
    imageContentView.transform = CGAffineTransform(a: CGFloat(cos(Double.pi * 0.5)), b: CGFloat(sin(Double.pi * 0.5)), c: CGFloat(-sin(Double.pi * 0.5)), d: CGFloat(cos(Double.pi * 0.5)), tx: 0, ty: 0)
  • 初始状态CGAffineTransformIdentity原理
    imageContentView.transform = CGAffineTransform.identity
    imageContentView.transform = CGAffineTransform(a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0)
  • CALayer的CATransform3D类型属性:animaView.layer.transform
    通过 .layer.transform 可以在3D模式下面的变化,通常使用的都是前缀为CATransform3D的类。
imageContentView.layer.transform = CATransform3DIdentity
UIView.animate(withDuration: 2.0) {
    imageContentView.layer.transform = CATransform3DMakeScale(2.0, 2.0, 1.0)
}

2.3 动画相关属性

2.3.1 UIView与动画相关的属性--与CGAffineTransform对应

    // animatable. do not use frame if view is transformed since it will not correctly reflect the actual location of the view. use bounds + center instead.
    open var frame: CGRect
    // use bounds/center and not frame if non-identity transform. if bounds dimension is odd, center may be have fractional part
    open var bounds: CGRect // default bounds is zero origin, frame size. animatable
    open var center: CGPoint // center is center of frame. animatable
    open var transform: CGAffineTransform // default is CGAffineTransformIdentity. animatable
    @available(iOS 4.0, *)
    open var contentScaleFactor: CGFloat //比例因子.这个属性代表了从逻辑坐标系转化成当前的设备坐标系的转化比例.
    open var isMultipleTouchEnabled: Bool // default is NO 这个属性是设置多手指触摸的. 但是并不能防止一个view上多个button同时点击. 因为它只是让touchbegin方法只有一个touch属性. 其余的都在event参数里面.
    open var isExclusiveTouch: Bool // default is NO 
  • frame. bounds. center的改变或者形变都可以用transform来取代frame.
  • 一般开发中都是 平移, 缩放,旋转的组合.

2.3.2 CALayer与动画相关的属性--与CATransform3D对应

    /* The bounds of the layer. Defaults to CGRectZero. Animatable. */
    /** Geometry and layer hierarchy properties. **/
    open var bounds: CGRect
    /* The position in the superlayer that the anchor point of the layer's
     * bounds rect is aligned to. Defaults to the zero point. Animatable. */
    open var position: CGPoint
    /* The Z component of the layer's position in its superlayer. Defaults
     * to zero. Animatable. */
    open var zPosition: CGFloat
    /* Defines the anchor point of the layer's bounds rect, as a point in
     * normalized layer coordinates - '(0, 0)' is the bottom left corner of
     * the bounds rect, '(1, 1)' is the top right corner. Defaults to
     * '(0.5, 0.5)', i.e. the center of the bounds rect. Animatable. */
    open var anchorPoint: CGPoint
    /* The Z component of the layer's anchor point (i.e. reference point for
     * position and transform). Defaults to zero. Animatable. */
    open var anchorPointZ: CGFloat
    /* A transform applied to the layer relative to the anchor point of its
     * bounds rect. Defaults to the identity transform. Animatable. */
    open var transform: CATransform3D
    /* Convenience methods for accessing the `transform' property as an
     * affine transform. */
    open func affineTransform() -> CGAffineTransform
    open func setAffineTransform(_ m: CGAffineTransform)
    /* Unlike NSView, each Layer in the hierarchy has an implicit frame
     * rectangle, a function of the `position', `bounds', `anchorPoint',
     * and `transform' properties. When setting the frame the `position'
     * and `bounds.size' are changed to match the given frame. */
    open var frame: CGRect

2.4 管理二维形变和三维形变的封装类:CGAffineTransform与CATransform3D

2.4.1 CGAffineTransform操作API

  • CGAffineTransform结构体定义
struct CGAffineTransform {
  CGFloat a, b, c, d;
  CGFloat tx, ty;
};

它其实表示的是一个矩阵:


仿射矩阵

因为最后一列总是是(0,0,1),所以有用的信息就是前面两列。对一个view进行仿射变化就相当于对view上的每个点做一个乘法,结果就是:


仿射矩阵计算公式

a表示x水平方向的缩放,tx表示x水平方向的偏移
d表示y垂直方向的缩放,ty表示y垂直方向的偏移
如果b和c不为零的话,那么视图肯定发生了旋转,旋转角度这样计算:tan(angle) = b / a

没有变化最初的样子

CGAffineTransform操作的数学本质

  • CGPoint转换公式


    UIView二维动画与CAAnimation核心动画_第3张图片
    CGPoint转换公式
  • 矩阵乘法运算原理演示


    UIView二维动画与CAAnimation核心动画_第4张图片
    矩阵乘法运算原理演示

2.4.2 CATransform3D操作API

//还原
 CATransform3DIdentity

 //位移3D仿射  ==> (CGFloat tx, CGFloat ty, CGFloat tz)
CATransform3DMakeTranslation
CATransform3DTranslation        
//旋转3D仿射 ==> (CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeRotation
CATransform3DRotation  
//缩放3D仿射 ==>  (CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale
CATransform3DScale
//叠加3D仿射效果
CATransform3DConcat    
//仿射基础3D方法,可以直接做效果叠加
CGAffineTransformMake (sx,shx,shy,sy,tx,ty)
//检查是否有做过仿射3D效果  == ((CATransform3D t))
CATransform3DIsIdentity(transform)
//检查2个3D仿射效果是否相同
CATransform3DEqualToTransform(transform1,transform2)
//3D仿射效果反转(反效果,比如原来扩大,就变成缩小)
CATransform3DInvert(transform)
2.4.3 CATransform3D与CGAffineTransform相互转换API
//将一个CGAffinrTransform转化为CATransform3D
CATransform3D CATransform3DMakeAffineTransform (CGAffineTransform m);
//判断一个CATransform3D是否可以转换为CAAffineTransformbool 
CATransform3DIsAffine (CATransform3D t);
//将CATransform3D转换为CGAffineTransform
CGAffineTransform CATransform3DGetAffineTransform (CATransform3D t);

2.5 “组合动画” 与 CGAffineTransformConcat

2.5.1 连接设置多个属性组合成一个动画

连接设置两个以上属性的动画,可以先调用含有formMake的API,然后再调用只含有form的API,如下

alertView.transform = CGAffineTransformMakeScale(.25, .25);
alertView.transform = CGAffineTransformTranslate(alertView.transform, 0, 600);

2.5.2利用CGAffineTransformConcat设置组合动画

另外,可以直接利用CGAffineTransformConcat来组合多种含有formMake的形变API.

CGAffineTransform viewTransform = CGAffineTransformConcat(CGAffineTransformMakeScale(.25, .25), CGAffineTransformMakeTranslation(0, 600));
alertView.transform = viewTransform;

关于组合3D形变也有相应的API, CATransform3DConcat

未完待续ing

你可能感兴趣的:(UIView二维动画与CAAnimation核心动画)