iOS SwiftUI 动画开发指南

iOS SwiftUI 动画开发指南

为了把大家吸引过来,先上几张动画效果图:

"引狼图"1 :按钮点击 - 果冻动画效果

"引狼图"2 :波纹动画效果

"引狼图"3 :Lottie复杂动画效果

1、动画概念

SwiftUI 的动画是基于声明式的,当视图的状态发生变化时,系统会自动在旧状态和新状态之间插入中间帧,创建平滑的过渡效果。

工作原理:

状态驱动:动画由状态变化触发

声明式语法:描述"做什么"而不是"怎么做"

自动插值:系统自动计算中间值

类型安全:编译时检查动画兼容性

1.1、隐式动画

隐式动画通过 .animation() 修饰符自动应用于视图的所有可动画属性变化。当修饰符观察的值发生变化时,所有可动画的属性都会以指定的动画方式过渡。

隐式动画特点:

  • 使用 .animation() 修饰符
  • 状态变化时自动动画
  • 代码简洁,声明式
swift 复制代码
import SwiftUI

struct SimpleImplicitAnimation: View {
    @State private var isTapped = false
    
    var body: some View {
        VStack(spacing: 30) {
            // 圆形视图 - 点击后会放大并变色
            Circle()
                .fill(isTapped ? .blue : .red)        // 颜色变化
                .frame(width: isTapped ? 150 : 100,   // 大小变化
                       height: isTapped ? 150 : 100)
                .animation(.easeInOut(duration: 0.5), value: isTapped) // 隐式动画
            
            Button("点击动画") {
                isTapped.toggle() // 状态变化自动触发动画
            }
        }
    }
}
1.2、显式动画

显式动画使用 withAnimation 闭包,只对闭包内的状态变化应用动画。可以精确控制哪些状态变化应该动画,以及使用什么样的动画参数。

显式动画特点:

  • 使用 withAnimation { } 闭包
  • 精确控制哪些变化有动画
  • 手动触发动画
swift 复制代码
struct SimpleExplicitAnimation: View {
    @State private var isTapped = false
    
    var body: some View {
        VStack(spacing: 30) {
            // 圆形视图
            Circle()
                .fill(isTapped ? .green : .orange)    // 颜色变化
                .frame(width: isTapped ? 150 : 100,   // 大小变化
                       height: isTapped ? 150 : 100)
            // 注意:这里没有 .animation() 修饰符
            
            Button("点击动画") {
                // 使用 withAnimation 显式包装状态变化
                withAnimation(.easeInOut(duration: 0.5)) {
                    isTapped.toggle() // 只有这个变化有动画
                }
            }
        }
    }
}
1.3、动画曲线

在 SwiftUI 中,动画曲线(也称为时序曲线或缓动函数)定义了动画随时间变化的速度和节奏。它们让动画看起来更自然、更流畅。

1.3.1、线性动画 (Linear)

匀速动画,速度保持不变。

swift 复制代码
.animation(.linear(duration: 1.0), value: value)

1.3.2、缓动动画 (Ease)

速度会变化,提供更自然的运动效果。

包含:缓入(.easeIn)、缓出(.easeOut)、缓入缓出(.easeInOut,默认)

swift 复制代码
.animation(.easeInOut(duration: 1.0), value: value)

// 开始慢 → 中间快 → 结束慢
// 最自然的运动曲线
swift 复制代码
.animation(.easeIn(duration: 1.0), value: value)

// 开始慢 → 逐渐加速到结束
swift 复制代码
.animation(.easeOut(duration: 1.0), value: value)

// 开始快 → 逐渐减速到结束

1.3.3、弹性动画 (Spring)

模拟弹簧物理效果,带有回弹。

swift 复制代码
.animation(
    .spring(
        response: 0.6,        // 持续时间
        dampingFraction: 0.8, // 阻尼(0-1,越小弹性越大)
        blendDuration: 0.25   // 混合持续时间
    ),
    value: value
)

1.3.4、交互式弹簧 (Interpolating Spring)

更精确控制弹簧物理参数的动画。

swift 复制代码
.animation(
   .interpolatingSpring(
       mass: 1.0,        // 质量:影响惯性
       stiffness: 100,   // 刚度:影响回弹力度
       damping: 10,      // 阻尼:影响阻力
       initialVelocity: 0 // 初始速度
     ),
   value: value
)

1.3.5、自定义动画曲线

timingCurve 是 SwiftUI 中用于创建自定义动画曲线的函数,它使用三次贝塞尔曲线来精确控制动画的加速和减速过程。

swift 复制代码
.animation(
   .timingCurve(
        0.25,   // 控制点1 X: 开始阶段的弯曲程度
        0.1,    // 控制点1 Y: 开始阶段的速度  
        0.25,   // 控制点2 X: 结束阶段的弯曲程度
        1.0,    // 控制点2 Y: 结束阶段的速度
        duration: 1.0  // 动画总时长
   	), 
   value: value
)
1.4、动画重复

1.4.1、使用 repeatCount 指定重复次数

swift 复制代码
struct RepeatCountExample: View {
    @State private var scale: CGFloat = 1.0
    
    var body: some View {
        Circle()
            .fill(Color.blue)
            .frame(width: 100, height: 100)
            .scaleEffect(scale)
            .onAppear {
                withAnimation(
                    .easeInOut(duration: 0.5)
                    .repeatCount(3) // 重复3次
                ) {
                    scale = 1.5
                }
            }
    }
}

1.4.2、使用 repeatForever 无限重复

swift 复制代码
struct RepeatForeverExample: View {
    @State private var rotation: Double = 0
    
    var body: some View {
        Rectangle()
            .fill(Color.red)
            .frame(width: 100, height: 100)
            .rotationEffect(.degrees(rotation))
            .onAppear {
                withAnimation(
                    .linear(duration: 1.0)
                    .repeatForever(autoreverses: false) // 无限重复,不自动反向
                ) {
                    rotation = 360
                }
            }
    }
}

1.4.3、带自动反向的重复

swift 复制代码
struct AutoreverseExample: View {
    @State private var offsetY: CGFloat = 0
    
    var body: some View {
        Circle()
            .fill(Color.green)
            .frame(width: 80, height: 80)
            .offset(y: offsetY)
            .onAppear {
                withAnimation(
                    .easeInOut(duration: 0.8)
                    .repeatForever(autoreverses: true) // 来回弹跳
                ) {
                    offsetY = 100
                }
            }
    }
}

2、基础动画示例

2.1、位移动画 (Move)
swift 复制代码
import SwiftUI

struct MoveAnimationSimple: View {
    @State private var moveRight = false
    
    var body: some View {
        VStack {
            Circle()
                .fill(.blue)
                .frame(width: 80, height: 80)
                .offset(x: moveRight ? 100 : 0) // X轴位移
                .animation(.easeInOut(duration: 1.0), value: moveRight)
            
            Button("移动") {
                moveRight.toggle()
            }
        }
    }
}
2.2、旋转动画 (Rotation)
swift 复制代码
struct RotationAnimationSimple: View {
    @State private var rotate = false
    
    var body: some View {
        VStack {
            Rectangle()
                .fill(.red)
                .frame(width: 100, height: 100)
                .rotationEffect(.degrees(rotate ? 180 : 0)) // 旋转角度
                .animation(.easeInOut(duration: 1.0), value: rotate)
            
            Button("旋转") {
                rotate.toggle()
            }
        }
    }
}
2.3、缩放动画 (Scale)
swift 复制代码
struct ScaleAnimationSimple: View {
    @State private var scaleUp = false
    
    var body: some View {
        VStack {
            Circle()
                .fill(.green)
                .frame(width: 80, height: 80)
                .scaleEffect(scaleUp ? 1.5 : 1.0) // 缩放比例
                .animation(.easeInOut(duration: 1.0), value: scaleUp)
            
            Button("缩放") {
                scaleUp.toggle()
            }
        }
    }
}
2.4、透明度动画 (Opacity)
swift 复制代码
struct OpacityAnimationSimple: View {
    @State private var show = false
    
    var body: some View {
        VStack {
            Circle()
                .fill(.orange)
                .frame(width: 100, height: 100)
                .opacity(show ? 1.0 : 0.3) // 透明度
                .animation(.easeInOut(duration: 1.0), value: show)
            
            Button("淡入/淡出") {
                show.toggle()
            }
        }
    }
}
2.5、颜色动画 (Color)
swift 复制代码
struct ColorAnimationSimple: View {
    @State private var changeColor = false
    
    var body: some View {
        VStack {
            Rectangle()
                .fill(changeColor ? .purple : .blue) // 颜色变化
                .frame(width: 120, height: 120)
                .animation(.easeInOut(duration: 1.0), value: changeColor)
            
            Button("变色") {
                changeColor.toggle()
            }
        }
    }
}
2.6、圆角动画 (Corner Radius)
swift 复制代码
struct CornerRadiusAnimationSimple: View {
    @State private var roundCorners = false
    
    var body: some View {
        VStack {
            Rectangle()
                .fill(.red)
                .frame(width: 120, height: 120)
                .cornerRadius(roundCorners ? 60 : 0) // 圆角变化
                .animation(.easeInOut(duration: 1.0), value: roundCorners)
            
            Button("圆角") {
                roundCorners.toggle()
            }
        }
    }
}

3、组合动画示例

3.1、多个属性同时动画

同时为视图的多个属性应用动画

swift 复制代码
struct CombinedAnimationView: View {
    @State private var isAnimating = false
    
    var body: some View {
        VStack(spacing: 30) {
            Text("组合动画")
                .font(.title)
            
            RoundedRectangle(cornerRadius: isAnimating ? 50 : 10)
                .fill(isAnimating ? .blue : .red)
                .frame(
                    width: isAnimating ? 200 : 100,
                    height: isAnimating ? 200 : 100
                )
                .rotationEffect(.degrees(isAnimating ? 180 : 0))
                .opacity(isAnimating ? 0.8 : 1.0)
                .animation(
                    .easeInOut(duration: 1.5),
                    value: isAnimating
                )
            
            Button("触发组合动画") {
                isAnimating.toggle()
            }
        }
        .padding()
    }
}
3.2、序列动画

使用多个 withAnimation 创建顺序执行的动画

此处模拟实现按钮点击 - 果冻动画效果:

swift 复制代码
struct Anim1: View {
    @State private var scaleX = 1.0
    @State private var scaleY = 1.0
    
    var body: some View {
        Button("button"){
            doAnim()
        }
        .foregroundColor(.white)
        .padding(.horizontal, 50)
        .padding(.vertical, 40)
        .background(Color.blue)
        .cornerRadius(10)
        .scaleEffect(x:scaleX, y: scaleY)
    
    }
    
    func doAnim(){
        
        Task{@MainActor in
            withAnimation(.spring(response:0.1, dampingFraction: 0.8)){
                scaleX = 0.8
                scaleY = 1.2
            }
            
            await try Task.sleep(for: .seconds(0.1))
            
            withAnimation(.interpolatingSpring(mass: 1.0, stiffness: 200, damping: 8)){
                scaleX = 1.0
                scaleY = 1.0
            }
        }
    }
}

4、Lottie动画

使用SwiftUI动画可实现基础的动画效果,如果动画包含复杂逻辑,建议使用Lottie实现(线上有很多的lottie动画文件(.json)可直接使用)

Lottie 是一个由 Airbnb 开发的开源库,用于在 iOS、Android 和 Web 上展示高质量的动画。使用 Lottie 动画能够为你的 SwiftUI 应用增添更丰富的交互和视觉效果,同时保持良好的性能和用户体验。

SwiftUI 中使用 Lottie 动画的一般步骤:

1、安装 Lottie 库:可以使用Swift Package Manager 或者 CocoaPods

2、SwiftUI 视图导入 Lottie 模块:import Lottie

3、Lottie 动画 JSON 文件添加到你的项目中

4、SwiftUI 视图中使用 LottieView加载动画文件

json文件管理、动画加载参考:

相关推荐
游戏开发爱好者82 小时前
iOS 上架要求全解析,App Store 审核标准、开发者准备事项与开心上架(Appuploader)跨平台免 Mac 实战指南
android·macos·ios·小程序·uni-app·iphone·webview
alengan3 小时前
ios支付
macos·ios·cocoa
00后程序员张3 小时前
混淆 iOS 类名与变量名的实战指南,多工具组合把混淆做成工程能力(混淆 iOS 类名变量名/IPA 成品混淆Ipa/Guard CLI 实操)
android·ios·小程序·https·uni-app·iphone·webview
MrZWCui4 小时前
iOS app语言切换
macos·ios·cocoa
晴天无痕5 小时前
iOS修改tabbar的背景图
macos·ios·cocoa
Digitally5 小时前
5 种无需 iTunes 将 iPad 照片传输到电脑的方法
ios·电脑·ipad
RollingPin5 小时前
iOS八股文之 组件化
ios·路由·router·组件化·imp·分层设计
大熊猫侯佩6 小时前
猿族代码战记:Mutex 升级版——守护 Swift 并发的“香蕉仓库”
swiftui·swift·apple
大熊猫侯佩6 小时前
Thread.sleep 与 Task.sleep 终极对决:Swift 并发世界的 “魔法休眠术” 揭秘
ios·swift·apple