如何在 SwiftUI 中创建动画

前言

在 SwiftUI 中如果想实现动画效果,你需要告诉 SwiftUI 关于视图的不同状态值,这样,SwiftUI 可以依赖这些状态值改变的时候来绘制动画。你可以使用下面的三个函数来实现动画效果:

  • withAnimation(::)
  • animation(_:value:)
  • animation(_:)

SwiftUI 动画化了许多内置视图修饰器产生的效果,比如那些设置比例或不透明度值的效果。你可以通过让你的自定义视图遵守 Animatable 协议,并告诉 SwiftUI 你想要动画的值来动画其他值。

基本使用

SwiftUI 通过它的 animation() 修饰符内置了对动画的支持。要使用此修饰符,请将其放置在视图的任何其他修饰符之后,告诉它你想要的动画类型,并确保给其指定了想动画的特定值,以便仅在该特定值更改时触发动画。

例如,下面这段代码创建了一个按钮,每次按下它,它都会变大一倍:

less 复制代码
struct ContentView: View {
    @State private var scale = 1.0

    var body: some View {
        Button("动画按钮") {
            scale += 1
        }
        .scaleEffect(scale)
        .animation(.linear(duration: 1), value: scale)
    }
}

效果图如下:

上面的动画会在一秒之内发生,如果你不想为动画指定精确的时间,你可以直接使用:.linear

Tips:从iOS 17及更高版本开始,SwiftUI 默认使用 spring 动画,但在此之前默认使用线性动画。

除了简单的线性动画,你还可以从各种内置选项中指定曲线,包括:

  • .easeIn 开始缓慢,然后加速,直到结束。
  • .easeOut 开始很快,然后加速,直到结束。
  • .easeInOut 开始缓慢,中间加速,接近尾声时减速。
  • .smooth 一个平滑的弹簧动画,具有预定义的持续时间和无反弹。(iOS 17)
  • .snappy 一个具有预定义持续时间和少量弹跳的弹簧动画,感觉更灵活。 (iOS 17)
  • .bouncy 一个具有预定义持续时间和较高弹跳量的弹簧动画。 (iOS 17)

你还可以对其他的修饰符进行动画操作,比如旋转、不透明度、边框等:

less 复制代码
struct ContentView: View {
    @State private var angle = 0.0
    @State private var borderThickness = 1.0

    var body: some View {
        Button("动画按钮") {
            angle += 45
            borderThickness += 1
        }
        .padding()
        .border(.red, width: borderThickness)
        .rotationEffect(.degrees(angle))
        .animation(.easeIn, value: angle)
    }
}

上面的代码会在按钮点击的时候使按钮旋转,并增加其边框。效果图如下:

自定义动画效果

当我们将 animation() 修饰符附加到视图上时,只要我们观察到的值发生变化,SwiftUI 就会使用默认的系统动画,自动将发生在该视图上的任何变化动画化。这意味着 iOS 将缓慢地开始动画,然后让它加快速度,直到它稍微超过目标值,最后它将以向后触碰直到达到最终状态结束。

iOS 默认选择 spring 动画,因为它们模仿了我们在现实世界中习惯的东西。但这些都是可以自定义的,比如你可以大致控制弹簧应该花多长时间来完成,以及弹簧应该有多大的弹性------它是否应该前后反弹,其中1是最大的弹性,0是没有弹性。比如下面的代码,这使我们的按钮迅速扩大,然后反弹很多:

less 复制代码
.animation(.spring(duration: 1, bounce: 0.9), value: animationAmount)

如果想更精确的控制,我们可以使用指定秒为单位的持续时间来定制动画。代码如下:

scss 复制代码
struct ContentView: View {
    @State private var animationAmount = 1.0

    var body: some View {
        Button("动画按钮") {
            animationAmount += 1
        }
        .padding(50)
        .background(.red)
        .foregroundStyle(.white)
        .clipShape(.circle)
        .scaleEffect(animationAmount)
        .animation(.easeInOut(duration: 2), value: animationAmount)
    }
}

上述代码可以实现一个持续两秒钟的开始缓慢,中间加速,接近尾声时减速的动画效果。效果如下:

相关推荐
做前端的娜娜子2 小时前
第二个 AI 调用——invoke 阻塞式与 stream 流式生成
人工智能·llm·掘金·金石计划
大熊猫侯佩4 小时前
WWDC 26 全新 ResultsObserver:终于能在 View 外面盯着 SwiftData 了
swiftui·swift·apple
做前端的娜娜子1 天前
第一个AI调用——TypeScript 工程初始化与配置管理(实战跟练)
langchain·openai·掘金·金石计划
东坡肘子1 天前
Apple Intelligence 已通过审核,即将在中国提供服务 -- 肘子的 Swift 周报 #148
人工智能·swiftui·swift
做前端的娜娜子3 天前
什么是浅拷贝(Shallow Copy)和深拷贝(Deep Copy)?如何手动实现一个深拷贝函数
javascript·面试·掘金·金石计划
做前端的娜娜子4 天前
JavaScript 闭包
前端·javascript·掘金·金石计划
做前端的娜娜子5 天前
移动端上拉加载与下拉刷新实现方案
前端·面试·掘金·金石计划
2501_915106326 天前
SwiftUI项目创建详解:使用Xcode从零开始创建第一个App项目
ide·vscode·ios·swiftui·个人开发·xcode·敏捷流程
东坡肘子8 天前
热茶还是冰咖啡 -- 肘子的 Swift 周报 #147
人工智能·swiftui·swift
做前端的娜娜子10 天前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划