1、iOS动画基础回顾:UIView动画与CALayer动画的核心区别,为什么说CALayer是动画的基石

说实话,很多iOS开发者做了两三年动画,还在用UIView.animate那一套。不是说不行,但你想过没有——为什么苹果要搞一个CALayer出来?

我个人习惯,在讲动画进阶之前,先把这个底层逻辑理清楚。因为后面30章的内容,几乎每一行代码都在跟CALayer打交道。你不懂它,后面会越学越懵。

1.1 UIView动画:简单,但有限

先看一个最常见的写法:

UIView.animate(withDuration: 0.3) {
    self.button.alpha = 0.0
    self.button.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
}

这段代码,说白了就是让按钮淡出并缩小。简单吧?但它的本质是什么?

UIView动画其实是对CALayer动画的一层封装。你调用UIView.animate时,系统内部会帮你创建一个CABasicAnimation,然后加到对应的layer上。嗯,这里要注意——UIView动画只能操作它暴露出来的那几个属性:frame、bounds、center、transform、alpha、backgroundColor。

我在项目中遇到过一个问题:想给视图加一个圆角变化的动画,UIView.animate直接不支持。当时我还傻傻地写了个定时器去逐帧修改layer.cornerRadius……后来才知道,直接用CABasicAnimation就一行代码的事。

核心结论:UIView动画是“阉割版”的CALayer动画。它方便,但能力有限。

1.2 CALayer动画:真正的底层能力

为什么说CALayer是动画的基石?

你想想看,屏幕上所有你能看到的东西,本质上都是CALayer在渲染。UIView只是一个事件响应的壳,真正负责绘制和动画的,是它底层的layer。

直接操作CALayer,你能做什么?

  • 任意属性的动画:不仅仅是UIView暴露的那几个,cornerRadius、borderWidth、shadowOffset、contents……几乎所有layer属性都能动
  • 更精细的控制:动画的起始值、结束值、时间曲线、重复次数、自动反转,全由你说了算
  • 组合动画:多个动画可以同时跑,也可以串行,甚至可以嵌套
  • 性能优势:CALayer动画在GPU上执行,不阻塞主线程

看一个对比:

能力 UIView动画 CALayer动画
可动画属性 约10个 全部可动画属性(约50+)
自定义时间曲线 有限(预置曲线) 完全自定义(CAMediaTimingFunction)
动画组合 不支持 CAAnimationGroup
逐帧控制 不支持 CADisplayLink + 手动设置
3D变换 不支持 CATransform3D

我曾经接手过一个老项目,里面用UIView动画做了一个复杂的卡片翻转效果。代码写了三百多行,各种嵌套闭包,bug还一堆。我花了一个下午,全部改成CATransform3D + CAAnimationGroup,代码缩到80行,流畅度还提升了一个档次。

1.3 为什么说CALayer是动画的基石?

说白了,iOS的动画体系就是三层结构:

  1. UIView动画:最上层,面向业务,简单易用
  2. CALayer动画:中间层,面向能力,灵活强大
  3. Core Animation:最底层,面向渲染,负责实际绘制

UIView动画最终会落到CALayer上,CALayer动画最终会交给Core Animation去渲染。所以不管你用哪一层,底层都是CALayer在干活。

我的建议:日常开发中,简单的alpha、transform变化用UIView动画没问题。但一旦涉及到圆角、阴影、边框、3D变换、复杂组合,直接上CALayer动画。别绕弯子。

1.4 一个实战对比:圆角动画

假设你要实现一个按钮,点击后圆角从10变成30。用UIView动画怎么写?

// ❌ 这样写没用
UIView.animate(withDuration: 0.3) {
    self.button.layer.cornerRadius = 30
}

你会发现圆角直接跳过去了,没有动画效果。为什么?因为UIView动画不认layer.cornerRadius这个属性。

正确的做法:

// ✅ 直接操作CALayer
let animation = CABasicAnimation(keyPath: "cornerRadius")
animation.fromValue = 10
animation.toValue = 30
animation.duration = 0.3
button.layer.add(animation, forKey: "cornerRadiusAnim")
button.layer.cornerRadius = 30 // 记得更新最终值

这里有个坑,我曾经踩过——动画结束后,layer的属性会恢复到动画开始前的值。所以一定要在动画结束后,手动把属性设置成目标值。或者用fillMode + removedOnCompletion来控制。

注意:CALayer动画默认是“隐式动画”,也就是说你直接改属性,它也会自动产生动画效果。但UIView的layer默认关闭了隐式动画,所以你需要显式创建CAAnimation。

1.5 小结

这一章我们聊了UIView动画和CALayer动画的核心区别。说白了,UIView动画是给“懒人”用的,CALayer动画才是真正干活的。后面的课程,我们会大量使用CALayer动画来实现各种酷炫效果。

记住一句话:所有UIView动画能做的事,CALayer都能做;但CALayer能做的事,UIView动画不一定能做。

下一章,我们会深入CAAnimation的继承体系,看看这些动画类到底是怎么工作的。到时候你会明白,为什么我说“理解了CALayer,你就理解了iOS动画的一半”。