iOS 核心动画

devtools/2024/12/22 21:46:09/
  • 核心动画
    • Layer 的自定义动画
    • Layer 自带的仿射动画
    • UIView 隐式动画
    • 显式动画
    • CAPropertyAnimation
      • CABasicAnimation
        • repeatCount
        • timingFunction
        • fillMode
        • delegate
        • 常见的 keyPath
        • CASpringAnimation
      • CAKeyframeAnimation
        • cacluationMode
        • rotationMode
        • 沿路径的动画
    • CATransition
    • UIView Animation
      • UIView(UIViewAnimation)
      • UIView(UIViewAnimationWithBlocks)
      • UIView (UIViewKeyframeAnimations)
    • CAAnimationGroup
    • 扩展阅读
      • CALayer
        • anchorPoint 和 position 的关系
        • CAShapeLayer
        • CAGradientLayer
      • CAMediaTiming 协议
        • CADisplayLink
      • CAMediaTimingFunction
    • 参考资料

当修改一个 CALayer 的属性时,它会通过-actionForKey:来查询属性对应的 action,而 key 就是对应的属性名称。

CAAnimation 遵守 CAAction 协议,返回的 action 其实就是一个 CAAnimation 动画对象。
一般默认返回的是 CABasicAnimation,默认动画时间0.25秒,动画速率函数为渐入渐出效果(kCAMediaTimingFunctionEaseInEaseOut)。

View 和 Layer 的关系:

所有的 View 都是由一个底层的 Layer 来驱动的,View 只是一个简单的控制器,View 的绝大多数属性都是从 Layer 对象中获取的数据。

AVCaptureVideoPreviewLayerCAShapeLayer是不需要附加到 View 上就可以在屏幕上显示内容。

参考:CALayer与UIView之间的关系

Layer 的自定义动画

可以通过监听自定义 Layer 的自定义属性值的变动,实现自定义动画。

  1. 监听自定义属性的 setter 方法,实现对应动画。

  2. 利用自定义属性的dynamic特性自行实现属性的 ivar 和 getter 方法,进行监听值的变化。

    通过重写+needsDisplayForKey: 方法可以监听属性值的变化。

    自定义属性必须是可进行插值(interpolate)的,否则无法获取属性的增量值。

    + (BOOL)needsDisplayForKey:(NSString *)key
    {// time 属性修改if ([@"time" isEqualToString:key]){// layer 的 display 函数会调用return YES;}return [super needsDisplayForKey:key];
    }- (void)display
    {// 使用 presentation Layer 来获取当前属性值// 而 model Layer 表示的是实际设置的属性最终值NSLog(@"time: %f", [[self presentationLayer] time]);// 使用 Core Graphics 函数绘制动画
    }// 当属性 time 变化时,添加新的动画
    - (id<CAAction>)actionForKey:(NSString *)key
    {if ([key isEqualToString:@"time"]){CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key];animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];animation.fromValue = @([[self presentationLayer] time]);return animation;}return [super actionForKey:key];
    }
    

    model Layer(模型层)和 presentation Layer(表示层)的区别:

    1. 前者负责数据的存储和获取。表示正在进行的动画结束时 layer 所达到的最终状态。
    2. 后者负责效果的展示,是前者的一个拷贝,表示的是当前的中间动画状态。

    属性赋值时修改的是 model Layer 中的值,下次屏幕刷新时 presentation Layer 的状态会回到 model Layer 的状态。

ObjC 中国 - Layer 中自定义属性的动画

Layer 自带的仿射动画

CGAffineTransform 形变就是把二维形变使用一个三维矩阵来表示,其中第三列总是(0,0,1),形变通过矩阵前两列来控制。

系统提供了 CGAffineTransformMake 结构体来控制形变。

动画函数意义
CGAffineTransformIdentitylayer 形变动画的还原(单位矩阵)
CGAffineTransformMakeTranslation平移动画(tx,ty)
CGAffineTransformMakeScale缩放动画(sx,sy)
CGAffineTransformMakeRotation旋转动画(angle)
CGAffineTransformTranslate实现在已存在的形变动画上的平移动画(t,tx,ty)
CGAffineTransformScale实现在已存在的形变动画上的缩放动画(t,sx,sy)
CGAffineTransformRotate实现在已存在的形变动画上的旋转动画(t,angle)
CGAffineTransformInvert实现形变动画的翻转效果(t)

CGAffineTransform _ Apple Developer Documentation
iOS CoreAnimation专题——实战篇(四)基于拖动手势的视图3D旋转效果 - CSDN博客

iOS 动画分为显式动画隐式动画

UIView 隐式动画

当我们修改界面的 CALayer 的可动画属性时,就会触发系统的隐式动画。
但是,系统中默认 UIView 对应的 CALayer 关闭了隐式动画。所以直接做出修改时无法产生效果的。

UIView 有一系列的 animatedWithDuration动画,可以使用 animation block 重新开启默认关闭的 Layer 动画。

当属性在动画 block 中被改变时,就附加了动画效果。

属性改变时 Layer 会向 View 请求一个动作,而一般情况下 View 将返回一个 NSNull。
只有当属性改变发生在动画 block 中时,系统会通过addAnimation:forKey:方法将动画添加到 Layer中, View 才会返回实际的动作。

显式动画

关键帧动画:最少含有起始帧、结束帧,中间的过渡效果由系统自动生成。

逐帧动画:周期性的调用绘制方法,绘制每帧的动画对象。比如,重写 UIView 的 - drawRect:方法来修改视图属性绘制视图。

iOS 的动画都是基于 CALayer 的,使用 CADisplayLink(基于屏幕刷新的,每次屏幕刷新都会触发调用)来周期性调用指定方法。

调用addAnimation:forKey:方法时,要注意设置 key 值。

设置 key 值后,系统再次执行该动画时会先检查该动画是否被创建。查找成功后会开头执行,失败后创建动画。

CAPropertyAnimation

当对属性赋值时,layer 会让它的 delegate 调用actionForLayer:forKey:方法获取一个返回值:

  1. 返回值可能为 nil,则 layer 会走自己的隐式动画。
  2. 返回值可能是 NSNull,则 layer 不会做任何动画。
  3. 返回值是一个实现了 CAAction 协议的对象,则 layer 会用该对象生成一个 CABaseAnimation 加到自身并执行动画。

CABasicAnimation

基本动画通过设置起始帧、结束帧的信息来实现动画效果。

属性解释
duration动画的持续时间
repeatCount动画持续次数(可以设置小数值,默认值为 0)
repeatDuration在设置的时间内,动画一直执行不计次数
beginTime指定动画开始的时间
timingFunction设置动画的速度变化
fillMode动画在开始和结束时的动作,默认值是 kCAFillModeRemoved
autoreverses动画结束时是否执行逆动画
fromValue所改变属性的起始值
toValue所改变属性的结束值
byValue所改变属性相同起始值的改变量
additive使 Core Animation 在更新 presentation layer 之前将动画的值添加到 model layer 中去。
对于所有形式的需要更新的元素可以重用相同的动画,且无需提前知道它们的位置。
默认值为 NO。
removedOnCompletion动画完成后其是否从渲染树中移除。默认值为 YES。
repeatCount

想要设置一直不断重复时,在 Swift 中使用Float.infinity,OC 中使用HUGE_VALF

timingFunction

用来设置时间段内动画的速度变化。可以使用 CAMediaTimingFunction 的 +functionWithControlPoints::::函数自定义速率函数。

使用 CAKeyFrameAnimation 时,可以使用 timingFunctions 来指定阶段性的速度变化函数。

  • kCAMediaTimingFunctionLinear

    在整个动画时间内动画都是以一个相同的速度来改变,即匀速运动。此值为 timingFunction 的默认值。

  • kCAMediaTimingFunctionEaseIn

    动画开始时较慢,之后动画会加速。

  • kCAMediaTimingFunctionEaseOut

    动画在开始时会较快,之后动画速度减慢。

  • kCAMediaTimingFunctionEaseInEaseOut

    动画在开始和结束时速度较慢,中间时间段内速度较快。

  • kCAMediaTimingFunctionDefault

    它和 kCAMediaTimingFunctionEaseInEaseOut 很类似,但是加速和减速的过程都稍微有些慢。

fillMode
  • kCAFillModeForwards

    动画开始之后 layer 的状态将保持在动画的最后一帧。

    可以用来表示动画变换后 layer 保持为最终状态(需要配合设置removedOnCompletion = NO)。
    当设置属性removedOnCompletion = YES时,此值无效。

  • kCAFillModeBackwards

    设置此值后将会立即执行动画的第一帧。

    设置此值会忽略 beginTime 属性。

  • kCAFillModeBoth

    该值是 kCAFillModeForwards 和 kCAFillModeBackwards 的组合状态。

  • kCAFillModeRemoved

    动画将在设置的 beginTime 开始执行,动画执行完成后会将 layer 的改变恢复原状。

delegate
  • animationDidStart::动画开始
  • animationDidStop:finished:动画结束
常见的 keyPath

CALayer 的 Animatable 属性直接赋值可以产生隐式动画,可以在 CAAnimation 的 keyPath 中设置。

keyPath值说明
transform.scale
transform.rotation
transform.translation
比例缩放
旋转状态
平移状态(具体方法同下)
transform.scale.x
transform.scale.y
缩放宽/高的比例
transform.rotation.x
transform.rotation.y
transform.rotation.z
围绕x/y/z轴旋转
backgroundColor背景颜色的变化
bounds大小缩放,中心不变
position位置(中心点改变)
contents内容变化(比如 UIImageView 的变化)
opacity透明度
contentsRect.size.width
contentsRect.size.height
横向/纵向拉伸缩放
shadowColor阴影的颜色
shadowOffset阴影的偏移
shadowOpacity阴影的不透明度
shadowRadius阴影渲染的模糊半径
cornerRadius绘制圆角时使用的半径
borderWidth边框的宽度
hidden隐藏过渡动画

CATransform3D Key Paths

CASpringAnimation

它继承于 CABaseAnimation,用于制作弹簧动画。

  • mass:质量,影响图层运动时弹簧惯性。质量越大,弹簧拉伸和压缩的幅度越大,动画的速度变慢,且波动幅度变大。
  • stiffness:刚度系数(劲度系数/弹性系数),该系数越大,形变产生的力越大,运动越快。
  • damping:阻尼系数,组织弹簧伸缩的系数,阻尼系数越大,停止越快。
  • initialVelocity:初始化速率。速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反。
  • settlingDuration:结算时间(只读),返回弹簧动画到停止时的估算时间。

该动画只在 iOS 9及其以后可用。

CAKeyframeAnimation

关键帧动画,关键帧动画就是在动画控制过程中开发者指定主要的动画状态,至于各种状态间动画如何进行则由系统自动运算补充(每个两个关键帧之间系统形成的动画成为补间动画)。

常见的设置模式:

  • 设置不同的属性进行关键帧控制
  • 绘制路径进行关键帧控制(优先级高于前者)

CABasicAnimation 只能从一个数值(fromValue)变到另一个数值(toValue),
而 CAKeyframeAnimation 可以使用数组保存一组数值。
CABasicAnimation 可以看做是最多只有2个关键帧的 CAKeyframeAnimation。

属性解释
values由许多关键帧值组成的数组用来进行动画。
(只有在 path 属性为 nil 时才有效)
path可以指定一个路径(CGPathRef/CGMutablePathRef),让动画沿着这个路径执行。
cacluationMode主要是指对平面中多个离散点的计算模式。
keyTimes一个包含若干 NSNumber 对象值的数组,用来指定对应的关键帧对应的时间点。
(NSNumber 的值在 0.0~1.0 之间,且后边的值要比前一个大或相等。
此值未设置时,各个关键帧的时间是平分的。)
timingFunctions设置动画的速度变化数组,类似于 values。
rotationMode设置旋转样式
duration动画在指定时间内完成
cacluationMode

设置平面中多个点的散列方式。

  • kCAAnimationLinear

    calculationMode 的默认值。

    可以配合 keyTimes 属性自定义线性时间来控制动画的效果(表示关键帧之间以线性进行差值计算)。

  • kCAAnimationDiscrete

    表示离散的,不进行差值计算,所有的关键帧直接逐个进行展示。

  • kCAAnimationPaced

    向对象添加一个恒定的速度,不论各个阶段线段有多长。设置此属性将忽略 keyTimes 属性。

  • kCAAnimationCubic

    平滑执行。对于位置变动的关键帧动画运行更加平滑。

  • kCAAnimationCubicPaced

    平滑均匀地执行。

rotationMode
  • kCAAnimationRotateAuto

    根据路径自动旋转。

  • kCAAnimationRotateAutoReverse

    根据路径自动翻转。

沿路径的动画

通过设置 path 属性代替 values 属性,设置动画。

CGPathRef 和 CGMutablePathRef 都是 CGPath 结构体类型的指针,前者是 const 类型不可修改,后者是可修改的。

创建 CGMutablePathRef 对象时,记得调用 CGPathRelease 方法释放。

常见的创建 Path 的方法:

  • UIGraphicsGetCurrentContext():获取当前绘图上下文

  • CGContextAddPath:将路径添加到绘图上下文中(绘图上下文,路径)

  • CGContextDrawPath:进行绘制(绘图上下文,绘制模式)

  • CGPathMoveToPoint:移动到某点(path,transform 变换,x,y)

  • CGPathAddLineToPoint:从当前点到目标点画线(path,transform 变换,x,y)

  • CGPathCreateMutable():创建可变 path 类

  • CGPathCreateWithRect:创建矩形路径(位置,transform 变换)

  • CGPathCreateWithRounderRect:创建矩形路径(位置,横向圆角,纵向圆角,transform 变换)

  • CGPathCreateWithEllipseInRect:创建椭圆形路径(位置,transform 变换)

  • CGPathCreateCopyByDashingPath:创建虚线路径(进行虚拟化的路径,transform 变换,从第几部分开始绘制虚线,风格数组,数组长度)

    CGFloat floats[] = {10,5};
    CGPathCreateCopyByDashingPath(pathRef, nil, 0, floats, 2);
    
  • CGPathAddEllipseInRect:椭圆形动画

    椭圆形动画(从右侧开始(0度)顺时针一周回到右侧)

  • CGPathCreateWithEllipseInRect:圆形动画

  • CGPathAddRect:矩形动画

    矩形动画(从左上角开始,顺时针运行一周回到最上角)

  • UIBezierPath

    • bezierPathWithRect:创建一个矩形
    • bezierPathWithRect:cornerRadius:创建一个圆角矩形
    • bezierPathWithOvalInRect:创建一个矩形内切圆(椭圆)
    • bezierPathWithArcCenter:radius:startAngle:endAngle:clockwise:画圆/圆弧(clockwise:是否顺时针,角度 0 表示圆的最右边那个点)
    • moveToPoint:
    • addQuadCurveToPoint:controlPoint:
    • addQuadCurveToPoint:controlPoint1:controlPoint2:

    贝塞尔曲线及常见效果

    中文版在线预览(更多效果)

CATransition

主要用于转场动画,即从一个场景以动画的形式过渡到另一个场景。

  • type:转场动画类型。

    API效果说明是否支持方向
    kCATransitionFade淡入效果
    kCATransitionMoveIn新视图移动到旧视图上
    kCATransitionPush新视图推出旧视图
    kCATransitionReveal移开旧视图显示新视图
    kCATransitionFromRight
    kCATransitionFromLeft
    kCATransitionFromTop
    kCATransitionFromBottom
    从右侧转场
    从左侧转场
    从顶部转场
    从底部转场
  • subtype:转场动画将要去往的方向。

  • startProgress/endProgress:开始和结束的位置进度,数值介于 0~1 之间,且结束值一定大于开始值。

UIView Animation

UIView(UIViewAnimation)

+ (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context;
+ (void)commitAnimations;
// 设置动画曲线
// UIViewAnimationCurveEaseInOut(默认值)
// UIViewAnimationCurveEaseIn
// UIViewAnimationCurveEaseOut
// UIViewAnimationCurveLinear
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve;
// 设置过渡动画类型
// UIViewAnimationTransitionNone
// UIViewAnimationTransitionFlipFromLeft
// UIViewAnimationTransitionFlipFromRight
// UIViewAnimationTransitionCurlUp
// UIViewAnimationTransitionCurlDown
+ (void)setAnimationTransition:(UIViewAnimationTransition)transition forView:(UIView *)view cache:(BOOL)cache;

UIView(UIViewAnimationWithBlocks)

为什么 UIView 的属性动画要设置在 block 内?

CALayer 中存在一个持有者 UIView 的 delegate。

当给 UIView 属性赋值时,除了调用其持有的 CALayer 对应的属性的 setter 方法外,CALayer 还会让其 delegate(即 UIView)调用actionForLayer:forKey:方法。

当在 block 外 设置属性时,此方法会返回 NSNull。
当在 block 内 设置属性时,此方法会返回正确的 CAAction。

UIView block动画实现原理

+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
// 默认delay为0,options为0
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
// 默认delay为0,options为0,completion为NULL
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations;// 阻尼动画
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;/*
* 转场动画
* 用于实现不能使用属性动画实现的动画效果
* UIViewAnimationOptions:通过控制参数实现不同的动画效果
*/
// 单视图的转场动画需要在动画块中设置视图转场前的内容和视图转场后的内容
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
// 增加toView至fromView.SuperView,从父视图移除fromView
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion;+ (void)performSystemAnimation:(UISystemAnimation)animation onViews:(NSArray *)views options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))parallelAnimations completion:(void (^ __nullable)(BOOL finished))completion;

UIView (UIViewKeyframeAnimations)

+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion;
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations;

CAAnimationGroup

通过同时为多个属性添加动画来完成一些复杂的效果。

  • 可以同时对视图的多个属性做出动画
  • 可以将所有的动画作为一个对象暴露出去
  • 可以同时控制动画组中的所有动画执行时间

Core Animation 编程指南

Facebook_pop(An extensible iOS and OS X animation library, useful for physics-based interactions)

扩展阅读

CALayer

CALayer 所共有的特点:可动画属性、隐式动画、transform 变形等。

类型含义
CAShapeLayer根据路径绘制矢量图形
CATextLayer绘制文字信息
CAGradientLayer绘制线性渐变色
CAEmitterLayer各种炫酷的粒子效果
CATransformLayer使用单独的图层创建 3D 图形
CATiledLayer将大图裁剪成多个小图以提高内存和性能
CAScrollLayer没有交互效果的滚动图层,没有滚动边界,可以任意滚动上面的图层内容
CAReplicatorLayer高效地创建多个相似的图层并施加相似的效果或动画
CAEAGLLayer用来显示任意的 OpenGL 图形
AVPlayerLayer用来播放视频
anchorPoint 和 position 的关系
  • position 是子图层的 anchorPoint 在父视图中的位置。

    anchorPoint 和 position 共同决定图层相对父图层的位置,即 frame 的 xy

    // origin 点的计算
    // anchorPoint 值默认为 (0.5, 0.5)
    frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
    frame.origin.y = position.y - anchorPoint.y * bounds.size.height;
    

    anchorPoint 是相对于自身的位置,而 position 是相对于父图层的位置。

  • 在图层旋转时的固定点。默认坐标点位置为:(0.5, 0.5)。

参考资料:

  • 详解CALayer的anchorPoint和position - 简书
  • 彻底弄清 anchorPoint 和 position - 简书
CAShapeLayer

CAShapeLayer 是一个通过矢量图形而不是 bitmap(位图)来绘制的 CALayer 子类。使用 CAShapeLayer 绘制路径有以下一些优点:

  • 渲染速度快。CAShapeLayer 使用了硬件加速,绘制图形比 Core Graphics 快很多。
  • 内存使用高效。CAShapeLayer 不需要创建寄宿图形(backing image),所以不会占用很大内存。
  • 不会被图层边界裁剪掉。CAShapeLayer 可以在边界外绘制,图层路径不会像使用 Core Graphics 的普通 CALayer 对象被裁剪掉。
  • 不会出现像素化。CAShapeLayer 做 3D 变换时,不会像其他有寄宿图形(backing image)的图层变得像素化。

矢量图和位图:

  • 位图:位图是通过排列像素点来构造的,像素点信息包括颜色(RGB) + 透明度(A)。常见的位图有 32 位位图和 24 位位图(不含 Alpha 通道)。

    位图在变形时会重新绘制每个像素点的信息,会造成图形的模糊。

    通过将复杂的绘制内容转换为位图数据,然后再由 GPU 渲染可以大幅提高位图的绘制效率。

  • 矢量图:矢量图是对多个进行布局,然后按照一定规则进行连线后形成的图形。矢量图的信息主要有点属性(包括点的坐标、连线顺序等)和线属性(包括线宽、描线颜色等)。

    矢量图在形变过程中,只会将点进行重新布局,然后按照点属性和线属性进行连线。

CAShapeLayer 中几个常见的 Animatable 的属性:

  • fillColor:填充颜色

  • strokeColor:描线颜色

  • strokeStart:表示描线开始的位置

  • strokeEnd:表示描线结束的位置

  • path:路径

    // fromPath 和 toPath 是两段不同的曲线
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
    animation.duration = 5;
    animation.fromValue = (__bridge id _Nullable)(fromPath.CGPath);//animation.toValue = (__bridge id _Nullable)(toPath.CGPath);
    // 直接修改 modelLayer 来替代 toValue
    shapeLayer.path = toPath.CGPath;
    [shapeLayer addAnimation:animation forKey:nil];
    

Animating the Drawing of a CGPath With CAShapeLayer – Ole Begemann

CAShapeLayer 与 mask 的结合使用:

mask 蒙版的常见实现方式有:使用图片做蒙版、使用 CAShapeLayer 绘制图形做蒙版。

mask 蒙版上还可以添加自定义动画效果。

// 使用图片作为蒙版
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(self.imageView.centerX /8.0f, self.imageView.centerY /7.f, self.imageView.width /1.5f, self.imageView.height /1.5f);
layer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"first"].CGImage);
self.imageView.layer.mask = layer;// 使用 CAShapeLayer 绘制图形作为蒙版
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = path.CGPath;
maskLayer.lineWidth = 10.0f;
// 填充颜色为透明时,填充内容不再作为蒙版内容
maskLayer.fillColor = [UIColor clearColor].CGColor;
// 不设置为透明色时,描线颜色就会作为蒙版内容
maskLayer.strokeColor = [UIColor redColor].CGColor;
self.imageView.layer.mask = maskLayer;// 为蒙版添加动画效果
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.duration = 3.0f;
animation.fromValue = @0;
[maskLayer addAnimation:animation forKey:nil];

iOS CoreAnimation专题——技巧篇(三)Layer Masking - 图层蒙版 - CSDN博客

CAGradientLayer

CAGradientLayer 可以实现线性渐变的效果,coreGraphics 可以绘制出更多渐变效果(实现复杂)。

  • colors:设置渐变颜色的数组
  • startPoint、endPoint:表示渐变的方向、渐变开始的地方和渐变结束的地方。
  • locations:渐变颜色所占比例。

将 CAGradientLayer 渐变效果与 mask 蒙版结合能够实现复杂的渐变效果,
将 Animation 动画加到 mask 蒙版上更能够实现复杂的渐变动画。

iOS CoreAnimation专题——实战篇(一)惊艳的进度条效果实现 - CSDN博客

CAMediaTiming 协议

  • repeatCount:动画重复次数,设置为HUGE_VALF时表示无限重复。

  • repeatDuration:动画总时长,如果大于单次时长则动画重复。

  • beginTime:开始时间。

    设置为 CACurrentMediaTime() +/- x 时:

    当为 + 时,延迟执行 x 秒。
    当为 - 时,直接执行 x 秒后的动画。

  • duration:单次动画时长。

  • speed:图层相对于父图层的速度,默认为1。

  • timeOffset:时间偏移量。

    设置偏移量时,动画会从绝对时间偏移相应的时间执行动画。比如:

    动画执行顺序为:1 => 2 => 3 => 4 => 5
    timeOffset 为2后的执行顺序为:3 => 4 => 5 => 1 => 2
    timeOffset 为4后的执行顺序为:5 => 1 => 2 => 3 => 4

    // speed 和 timeOffset 的配合使用可以用来控制动画的时间轴
    - (void)animationForColorViewBackgroundColor {CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];animation.fromValue = (__bridge id _Nullable)([UIColor orangeColor].CGColor);animation.toValue = (__bridge id _Nullable)([UIColor blueColor].CGColor);animation.duration = 1.0f;[self.colorView.layer addAnimation:animation forKey:@"backgroundColor"];// 暂时停止动画self.colorView.layer.speed = 0.0;
    }// 根据 slider 的值控制背景色渐变效果
    - (IBAction)sliderValueChanged:(UISlider *)sender {NSLog(@"current slider value:%.2f",sender.value);// 设置动画时间轴偏移量self.colorView.layer.timeOffset = sender.value;
    }
    
  • repeatCount:

  • repeatDuration:重复的时间间隔,优先级大于repeatCount。

  • autoreverses:是否自动翻转动画。

  • fillMode:动画在开始和结束时的动作。

动画的暂停和恢复:

// 暂停动画
- (void)pauseLayer:(CALayer *)layer {CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];layer.speed = 0.0;layer.timeOffset = pausedTime;
}
// 恢复动画
- (void)resumeLayer:(CALayer *)layer {CFTimeInterval pausedTime = layer.timeOffset;layer.speed = 1.0;layer.timeOffset = 0.0;layer.beginTime = 0.0;CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;layer.beginTime = timeSincePause;
}

Controlling Animation Timing(控制动画时机)

FPS(frame per second):帧率,也就是屏幕每秒钟刷新次数。如果帧率为60,表示屏幕每秒刷新60次,但是并不是每次屏幕刷新的时间间隔都是平均的。

CADisplayLink:就是屏幕保持 >60 fps 的帧率进行刷新,每次刷新都会根据绘制信息重绘屏幕上的显示内容。

// 创建 CADisplayLink 对象
+ (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;
- (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode;
- (void)removeFromRunLoop:(NSRunLoop *)runloop forMode:(NSRunLoopMode)mode;
// 停止刷新
- (void)invalidate;// 可以通过属性的设置来控制屏幕刷新的暂停与开始
@property(getter=isPaused, nonatomic) BOOL paused;

系统默认提供了两个常用的runloop mode:NSDefaultRunloopMode 和 NSRunLoopCommonModes。

一个 runloop只能在某个 mode 中运行,runloop 可以在多个不同 mode 间进行切换。

iOS CoreAnimation专题——实战篇(三)CADisplayLink高级应用:让视图的四条边振动起来 - CSDN博客

CAMediaTimingFunction

常见的 timingFunction:

  • kCAMediaTimingFunctionLinear
  • kCAMediaTimingFunctionEaseIn
  • kCAMediaTimingFunctionEaseOut
  • kCAMediaTimingFunctionEaseInEaseOut
  • kCAMediaTimingFunctionDefault
// 创建自定义的动画速率变化函数
+ (instancetype)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
- (instancetype)initWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;

CAMediaTimingFunction 效果查看器


参考资料

  • Core Animation 编程指南(官方)
  • iOS动画详解(学习动画看这一篇就够了) - CocoaChina_让移动开发更简单
  • iOS CoreAnimation专题——总览篇 - CSDN博客
  • iOS 动画分享(关键帧动画和波浪动画) - CocoaChina_让移动开发更简单
  • 动画黄金搭档 CADisplayLink & CAShapeLayer - CocoaChina_让移动开发更简单
  • iOS 核心动画高级技巧
  • iOS 保持界面流畅的技巧 - Garan no dou

⬅️ Back

⬆️ 回到顶部 ⬆️


http://www.ppmy.cn/devtools/144140.html

相关文章

Python中使用单例模式创建类

在 Python 中&#xff0c;实现单例模式有多种方式&#xff0c;每种方式都有其优缺点。先上结论&#xff0c;如果对某种实现方式有兴趣的话可以选择性的阅读。 1. 结论 实现方式优点缺点荐语元类线程安全&#xff0c;灵活实现复杂适合需要灵活性和线程安全的场景threading.Lock…

Pytorch | 从零构建Vgg对CIFAR10进行分类

Pytorch | 从零构建Vgg对CIFAR10进行分类 CIFAR10数据集Vgg网络结构特点性能应用影响 Vgg结构代码详解结构代码代码详解特征提取层 _make_layers前向传播 forward 训练过程和测试结果代码汇总vgg.pytrain.pytest.py 前面文章我们构建了AlexNet对CIFAR10进行分类&#xff1a; Py…

【计组】实验三 ORI指令设计实验

一、实验目的 1. 理解MIPS处理器指令格式及功能。 2. 掌握ori指令格式与功能。 3. 掌握ModelSim和ISE\Vivado工具软件。 4. 掌握基本的测试代码编写和FPGA开发板使用方法。 二、实验环境 1. 装有ModelSim和ISE\Vivado的计算机。 2. Sword\Basys3\EGo1实验系统。 三、实…

TDengine 新功能 从 CSV 批量创建子表

1. 背景 我们在从一些数据源&#xff08;比如关系型数据库&#xff09;批量导入数据前&#xff0c;我们可能需批量创建出所需子表。TDengine 引擎从 v3.3.3.0 版本开始&#xff0c;提供了通过 CSV 文件批量创建子表的功能&#xff0c;使用者只要按约定的格式生成 CSV 文件&…

半导体制造技术导论(第二版)萧宏 第十二章 化学机械研磨工艺

本章要求 1.列出化学机械研磨工艺的应用 化学机械研磨是一种移除工艺技术&#xff0c;结合化学反应和机械研磨去除沉积的薄膜&#xff0c;使表面更加平滑和平坦&#xff1b;也用于移除表面上大量的电介质薄膜&#xff0c;并在硅衬底上形成浅沟槽隔离STI&#xff1b;还可以从晶圆…

ORA-01114 ORA-27072 错误处理方法

Oracle数据库的临时表空间主要用于存储排序操作和其他临时数据&#xff0c;随着时间的推移&#xff0c;这些数据可能会累积并占用大量磁盘空间。 一、问题说明 在创建一张大表的索引时&#xff0c;出现如下错误。 一般出现这种错误&#xff0c;很可能是数据库临时表空间满了或…

Redis(2)常用命令

安装Redis 现在我们安装Redis 5&#xff0c;Redis安装在Linux上面安装&#xff0c;如果想在本机上面安装多个Redis的话&#xff0c;就要使用Docker。 在Ubuntu上面安装&#xff1a; 切换到root用户使用apt命令搜索相关的软件包&#xff08;apt search redis&#xff09;apt …

PHPstudy中的数据库启动不了

法一 netstat -ano |findstr "3306" 查看占用该端口的进程号 taskkill /f /pid 6720 杀死进程 法二 sc delete mysql