1. 让界面活起来的魔法:UIView动画

1.1 基础动画与实用场景

在登录页面中,当用户输入错误时,我们可以让输入框抖动起来。这种即时反馈的场景非常适合UIView动画:

// 抖动动画(技术栈:UIKit + UIView.animate)
func shakeTextField(_ textField: UITextField) {
    UIView.animate(
        withDuration: 0.05,
        delay: 0,
        options: [.autoreverse, .curveEaseInOut],
        animations: {
            // X轴左右移动10单位
            textField.transform = CGAffineTransform(translationX: 10, y: 0)
        },
        completion: { _ in
            // 恢复原始位置
            UIView.animate(withDuration: 0.05) {
                textField.transform = .identity
            }
        }
    )
}

这个示例适用于表单验证的即时反馈场景,通过快速的抖动动作吸引用户注意力。实际项目中可以将函数封装成扩展方法复用。

1.2 复杂布局动画实战

商品详情页的购物车图标入场动画可以这样实现:

// 购物车入场动画(技术栈:UIKit + Spring动画)
func cartIconEntrance() {
    cartView.alpha = 0
    cartView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
    
    UIView.animate(
        withDuration: 0.8,
        delay: 0,
        usingSpringWithDamping: 0.6,
        initialSpringVelocity: 0.8,
        options: [.curveEaseInOut],
        animations: {
            self.cartView.alpha = 1
            self.cartView.transform = .identity
        },
        completion: nil
    )
}

弹簧动画参数调节技巧:

  • damping(阻尼):值越小弹性越大
  • velocity(初速度):控制动画启动速度
  • 组合使用可模拟真实物理效果

1.3 注意事项与实战技巧

在用户快速滑动图片轮播时,建议使用.allowUserInteraction选项保证动画不阻塞手势:

// 轮播图翻页动画(带用户交互支持)
UIView.animate(
    withDuration: 0.3,
    delay: 0,
    options: [.curveEaseInOut, .allowUserInteraction],
    animations: {
        currentImageView.alpha = 0
        nextImageView.alpha = 1
    },
    completion: nil
)

2. 性能与特效兼备:Core Animation深度探索

2.1 核心动画属性实战

实现雷达波纹效果时,CAShapeLayer结合CAAnimationGroup是更好的选择:

// 雷达波纹动画(技术栈:Core Animation)
func createRippleEffect() {
    let shapeLayer = CAShapeLayer()
    shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 80, height: 80)).cgPath
    shapeLayer.fillColor = UIColor.blue.withAlphaComponent(0.3).cgColor
    
    let scaleAnim = CABasicAnimation(keyPath: "transform.scale")
    scaleAnim.fromValue = 0.8
    scaleAnim.toValue = 2.5
    
    let alphaAnim = CABasicAnimation(keyPath: "opacity")
    alphaAnim.fromValue = 1
    alphaAnim.toValue = 0
    
    let group = CAAnimationGroup()
    group.duration = 2
    group.repeatCount = .infinity
    group.animations = [scaleAnim, alphaAnim]
    
    shapeLayer.add(group, forKey: "rippleEffect")
    view.layer.addSublayer(shapeLayer)
}

这个案例演示了:

  • 通过多个动画属性组合实现复杂效果
  • 无限循环动画的实现方法
  • 图层合成的性能优势

2.2 关键帧动画实战

实现加载进度条的"弹性缓冲"效果:

// 弹性进度条动画(技术栈:Core Animation + CAKeyframeAnimation)
func elasticProgressAnimation() {
    let anim = CAKeyframeAnimation(keyPath: "strokeEnd")
    anim.values = [0, 0.7, 0.9, 1.0]       // 关键帧数值
    anim.keyTimes = [0, 0.3, 0.7, 1.0]    // 对应时间节点
    anim.timingFunctions = [
        CAMediaTimingFunction(name: .easeOut),
        CAMediaTimingFunction(name: .easeIn),
        CAMediaTimingFunction(controlPoints: 0.5, 1.8, 1.0, 1.0)
    ]
    anim.duration = 1.5
    progressLayer.add(anim, forKey: nil)
}

2.3 性能优化指南

在列表滚动时暂停复杂动画提升流畅度:

// 列表滚动时管理动画
func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let paused = scrollView.isTracking || scrollView.isDecelerating
    view.layer.speed = paused ? 0 : 1
}

3. 指尖上的舞蹈:手势交互与动画融合

3.1 基础拖拽动画实现

可拖动悬浮按钮的实现:

// 悬浮按钮拖拽效果(技术栈:UIKit手势识别)
@objc func handlePan(_ gesture: UIPanGestureRecognizer) {
    guard let targetView = gesture.view else { return }
    
    let translation = gesture.translation(in: view)
    targetView.center.x += translation.x
    targetView.center.y += translation.y
    gesture.setTranslation(.zero, in: view)
    
    // 添加跟随物理效果
    if gesture.state == .ended {
        let velocity = gesture.velocity(in: view)
        UIView.animate(
            withDuration: 0.5,
            delay: 0,
            usingSpringWithDamping: 0.7,
            initialSpringVelocity: velocity.y / 1000,
            options: [.curveEaseOut],
            animations: {
                targetView.center = self.originalPosition
            },
            completion: nil
        )
    }
}

3.2 组合手势实战案例

图片查看器的双指旋转缩放实现:

// 双指手势处理(技术栈:UIGestureRecognizerDelegate)
func setupPinchRotationGesture() {
    let pinch = UIPinchGestureRecognizer(target: self, action: #selector(handlePinch))
    let rotation = UIRotationGestureRecognizer(target: self, action: #selector(handleRotation))
    pinch.delegate = self
    rotation.delegate = self
    
    imageView.addGestureRecognizer(pinch)
    imageView.addGestureRecognizer(rotation)
}

@objc func handlePinch(_ gesture: UIPinchGestureRecognizer) {
    guard let view = gesture.view else { return }
    view.transform = view.transform.scaledBy(x: gesture.scale, y: gesture.scale)
    gesture.scale = 1
}

@objc func handleRotation(_ gesture: UIRotationGestureRecognizer) {
    guard let view = gesture.view else { return }
    view.transform = view.transform.rotated(by: gesture.rotation)
    gesture.rotation = 0
}

func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, 
                      shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
    return true  // 允许同时识别多个手势
}

4. 技术方案选择指南

应用场景对照表

场景特征 推荐技术
简单属性变化 UIView动画
需要物理模拟效果 UIKit弹簧动画
复杂路径/3D变换 Core Animation
需要精准控制动画进度 CAKeyframeAnimation
高性能粒子效果 Core Animation图层树

技术选型对比

UIView动画优势:

  • 语法简洁,快速开发
  • 自动处理动画冲突
  • 支持布局约束动画

Core Animation优势:

  • 60FPS流畅动画
  • 精细控制动画属性
  • 支持图层蒙版等高级特性

5. 专家级注意事项清单

  1. 内存管理: 对CALayer的强引用可能导致内存泄漏
  2. 主线程规则: 所有动画操作必须在主线程执行
  3. 逆向动画陷阱: 使用autoreverse时需要手动重置属性
  4. 性能监控: 使用CADisplayLink检测帧率变化
  5. 混合方案: 同一视图中避免同时使用UIView和Core Animation

6. 总结与展望

在实际项目中,我经常看到开发者盲目追求复杂动画导致性能问题。通过组合使用UIKit和Core Animation,我们可以根据场景选择最佳方案。例如:在电商类APP的商品详情页,用UIKit实现图片轮播的过渡动画,同时在购物车图标使用Core Animation实现粒子效果。

未来的发展方向可能包括:与SwiftUI动画的整合、Lottie动画库的混合使用,以及Metal加速的特效实现。但无论技术如何演进,理解底层原理始终是构建优秀动效的关键。